Browse Source

Merge pull request #5 from PascalCoinDev/master

Merge to 5.5
UrbanCohortDev 4 years ago
parent
commit
04edf3fcad
62 changed files with 10621 additions and 1525 deletions
  1. 43 3
      CHANGELOG.md
  2. 4 1
      PIP/PIP-0016.md
  3. 12 12
      PIP/PIP-0027.md
  4. 8 3
      PIP/PIP-0033.md
  5. 2 2
      src/config.inc
  6. 106 41
      src/core/UAccounts.pas
  7. 5 2
      src/core/UBlockChain.pas
  8. 11 5
      src/core/UConst.pas
  9. 292 117
      src/core/UEPasa.pas
  10. 298 0
      src/core/UEPasaDecoder.pas
  11. 1 1
      src/core/UEncoding.pas
  12. 16 11
      src/core/UNetProtocol.pas
  13. 239 3
      src/core/UNode.pas
  14. 1 1
      src/core/UOpTransaction.pas
  15. 182 229
      src/core/UPCAbstractMem.pas
  16. 25 48
      src/core/UPCAbstractMemAccountKeys.pas
  17. 214 0
      src/core/UPCAbstractMemAccounts.pas
  18. 245 0
      src/core/UPCAccountsOrdenations.pas
  19. 32 0
      src/core/UPCOrderedLists.pas
  20. 180 0
      src/core/UPCRPCFileUtils.pas
  21. 24 12
      src/core/UPCRPCFindAccounts.pas
  22. 105 32
      src/core/UPCRPCOpData.pas
  23. 362 0
      src/core/UPCRPCSend.pas
  24. 66 28
      src/core/UPCTNetDataExtraMessages.pas
  25. 76 0
      src/core/UPCTemporalAbstractMem.pas
  26. 5 7
      src/core/UPoolMinerThreads.pas
  27. 431 188
      src/core/URPC.pas
  28. 107 26
      src/core/USettings.pas
  29. 6 1
      src/core/UTCPIP.pas
  30. 1 1
      src/core/UTxMultiOperation.pas
  31. 2 2
      src/core/UWallet.pas
  32. 3 5
      src/core/upcdaemon.pas
  33. 3914 0
      src/gui-classic/UFRMAskForAccount.dfm
  34. 161 0
      src/gui-classic/UFRMAskForAccount.pas
  35. 38 16
      src/gui-classic/UFRMOperation.dfm
  36. 39 16
      src/gui-classic/UFRMOperation.lfm
  37. 383 241
      src/gui-classic/UFRMOperation.pas
  38. 3 1
      src/gui-classic/UFRMOperationsExplorer.pas
  39. 4 5
      src/gui-classic/UFRMPascalCoinWalletConfig.pas
  40. 1025 0
      src/gui-classic/UFRMSplash.dfm
  41. 184 0
      src/gui-classic/UFRMSplash.pas
  42. 0 12
      src/gui-classic/UFRMWallet.dfm
  43. 20 19
      src/gui-classic/UFRMWallet.lfm
  44. 117 151
      src/gui-classic/UFRMWallet.pas
  45. 1 1
      src/gui-classic/UFRMWalletKeys.pas
  46. 77 40
      src/gui-classic/UGridUtils.pas
  47. 2 1
      src/libraries/abstractmem/ConfigAbstractMem.inc
  48. 429 143
      src/libraries/abstractmem/UAbstractBTree.pas
  49. 536 0
      src/libraries/abstractmem/UAbstractMemBTree.pas
  50. 3 1
      src/libraries/abstractmem/tests/AbstractMem.Tests.dpr
  51. 28 14
      src/libraries/abstractmem/tests/src/UAbstractBTree.Tests.pas
  52. 346 0
      src/libraries/abstractmem/tests/src/UAbstractMemBTree.Tests.pas
  53. 21 0
      src/libraries/pascalcoin/UAppParams.pas
  54. 20 0
      src/libraries/pascalcoin/UJSONFunctions.pas
  55. 9 0
      src/libraries/sphere10/UCommon.pas
  56. 4 0
      src/pascalcoin_daemon.ini
  57. 1 1
      src/pascalcoin_daemon.lpi
  58. 13 5
      src/pascalcoin_miner.lpi
  59. 31 13
      src/pascalcoin_miner.pp
  60. 51 32
      src/pascalcoin_wallet_classic.dpr
  61. 50 31
      src/pascalcoin_wallet_classic.dproj
  62. 7 1
      src/pascalcoin_wallet_classic.lpi

+ 43 - 3
CHANGELOG.md

@@ -1,22 +1,62 @@
 # Changelog
 
-## Build 5.4 - (PENDING RELEASE)
+## Build 5.4 - 2021-03-24
 - Added usage of AbstractMem library to allow build a PascalCoin version using virtual memory and efficient caching mechanism
-  - Must activate {$DEFINE USE_ABSTRACTMEM} at config.inc file
+  - Use AbstractMem library v1.2
+  - Must activate {$DEFINE USE_ABSTRACTMEM} at config.inc file (Enabled by default)
+- Added "Ask for Account (PASA)" feature on GUI wallet
+- Implementation of PIP-0027 (E-PASA: Infinite Address-Space Layer-2) -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0027.md  
 - Changes to `pascalcoin_daemon.ini` file:
   - Added "DATAFOLDER" configuration option at pascalcoin_daemon.ini file (daemon/service) in order to allow customize data folder
   - Added "ABSTRACTMEM_MAX_CACHE_MB" to customize Maximum megabytes in memory as a cache
   - Added "ABSTRACTMEM_USE_CACHE_ON_LISTS","ABSTRACTMEM_CACHE_MAX_ACCOUNTS","ABSTRACTMEM_CACHE_MAX_PUBKEYS" in order to customize cache values
+  - Added "MAX_PAYTOKEY_MOLINAS" to fix limit on automatic PayToKey feature
 - Improved performance when downloading Safebox (Fresh installation)
 - JSON-RPC changes:  
+  - Updated "Operation Object" and "Multi Operation Object" return values:
+    **(IF THE WALLET IS UNLOCKED Will automatically try to decrypt encoded payloads and also return E-PASA used)**
+    - "senders" or "receivers" : ARRAY
+      - "account_epasa" : (String) If operation was using valid E-PASA format and can be decoded, will return E-PASA format used with extended checksum
+      - "unenc_payload" : (String) If payload can be decoded returns unencoded value in readable format (no HEXASTRING)
+      - "unenc_hexpayload" : (HEXASTRING) Unencoded value in hexastring
+      - "payload_method" : (String) Can be "key" or "pwd"
+      - "enc_pubkey" : HexaString with public key (if "payload_method"="key")
+      - "pwd" : String with password used (if "payload_method"="pwd")
+  - New method "checkepasa": Check that "account_epasa" param contains a valid E-PASA format. Returns an "EPasa Object" (See below)
+  - New method "validateepasa": Creates an "account_epasa" with provided data and returns an "EPasa Object" (See below)
+    - "account" : Valid number or account name  ( Use @ for a PayToKey )
+    - "payload_method" : "none","dest","sender","aes"
+    - "pwd" : If "payload_method" = "aes"
+    - "payload_encode" : "string"(default) | "hexa" | "base58"
+    - "payload" : HEXASTRING with the payload data
+  - "EPasa Object" params:
+    - "account_epasa" : (String) Encoded EPASA with extended checksum
+    - "account" : number or name 
+    - "payload_method" : "none","dest","sender","aes"
+    - "pwd" : (String) Provided only if "payload_method" = "aes"
+    - "payload_encode" : "string"(default) | "hexa" | "base58"
+    - "account_epasa_classic" : (String) Encoded EPASA without extended checksum
+    - "payload" : HEXASTRING with the payload data
+    - "payload_type" : Byte
+    - "is_pay_to_key" : (Boolean) True if EPasa is a Pay To Key format like @[Base58Pubkey]
+  - Payload encoding will automatically set "payload_type" value based on encoding params in order to store E-PASA standard
   - Updated "findaccounts": 
-    -New param "end" (integer, -1 for default): Will search from "start" to "end" (if "end"=-1 will search to the end)
+    - New param "end" (integer, -1 for default): Will search from "start" to "end" (if "end"=-1 will search to the end)
   - New method "findblocks": Will search and return an array of "Block objects"
     - "start","end","max" : Based on block number and max returns values (max by default=100)
     - "enc_pubkey" or "b58_pubkey" : If provided will return blocks where pubkey equal to provided
     - "payload", "payloadsearchtype" : Same workaround than "name" and "namesearchtype" on "findaccounts" method  
+  - New method "save-safebox-stream" : Will save a Safebox file in Stream format
+    - "filename" : String (optional)
+  - New method "save-safebox-abstractmem" : Will save a Safebox AbstractMem file of actual state
+    - "filename" : String (optional)
+  - New method "abstractmem-stats" : Testing purposes only
+  - Updated "addnode"
+    - New param "whitelist" : Boolean (False by default). When true the "nodes" ips will be added to Whitelist for JSON-RPC calls
 - Fixed bugs:
   - Fixed bugs on "pascalcoin_daemon" (daemon on Linux / Service on Windows) that produced crash on windows and some invalid finalization on Linux
+  - Fixed bugs on "finddataoperations" (not searching as expected)
+  - Fixed bug on "delistaccountforsale" (Freezing application / api calls)
   - Fixed minor bugs
 
 ## Build 5.3.0 - 2020-03-12

+ 4 - 1
PIP/PIP-0016.md

@@ -55,4 +55,7 @@ As mentioned in Motivation section, enveloping layer-2 protocols inside layer-1
 
 ## Backwards Compatibility
 
-This change is not backwards compatible and requires a hard-fork activation.
+This change is not backwards compatible and requires a hard-fork activation.
+
+## Related links
+- PIP-0033 DATA operation RPC implementation https://www.pascalcoin.org/development/pips/pip-0033

+ 12 - 12
PIP/PIP-0027.md

@@ -182,47 +182,47 @@ PayloadType will allow E-PASA's to be deterministically decoded by the recipient
 		/// <summary>
 		/// Payload encryption and encoding method not specified.
 		/// </summary>
-		NonDeterministic = 0x00000000,
+		NonDeterministic = 0b00000000,
 
 		/// <summary>
 		/// Unencrypted, public payload.
 		/// </summary>
-		Public = 0x00000001,
+		Public = 0b00000001,
 
 		/// <summary>
 		/// ECIES encrypted using recipient accounts public key.
 		/// </summary>		
-		RecipientKeyEncrypted = 0x00000010,
+		RecipientKeyEncrypted = 0b00000010,
 
 		/// <summary>
 		/// ECIES encrypted using sender accounts public key.
 		/// </summary>
-		SenderKeyEncrypted = 0x00000100,
+		SenderKeyEncrypted = 0b00000100,
 
 		/// <summary>
 		/// AES encrypted using pwd param
 		/// </summary>
-		PasswordEncrypted = 0x00001000,
+		PasswordEncrypted = 0b00001000,
 
 		/// <summary>
 		/// Payload data encoded in ASCII
 		/// </summary>
-		AsciiFormatted = 0x00010000,
+		AsciiFormatted = 0b00010000,
 
 		/// <summary>
 		/// Payload data encoded in HEX
 		/// </summary>
-		HexFormatted = 0x00100000,
+		HexFormatted = 0b00100000,
 
 		/// <summary>
 		/// Payload data encoded in Base58
 		/// </summary>
-		Base58Formatted = 0x01000000,
+		Base58Formatted = 0b01000000,
 
 		/// <summary>
 		/// E-PASA addressed by account name (not number).
 		/// </summary>
-		AddressedByName = 0x10000000,
+		AddressedByName = 0b10000000,
 
 	}
 ```
@@ -383,12 +383,12 @@ For Layer-2 applications the ability for a receiver to auto-decode the E-PASA vi
 * Ugochukwu Mmaduekwe for assistance developing payload length validation rules
 * Benjamin Ansbach for regular feedback, assistance and insightful suggestions
 * UrbanCohort for elegancy-improving suggestion
- 
+
 ## Reference Implementation
 
 The following regex parses an e-pasa:
 ```
-((?<AccountNumber>(0|[1-9]\d+))(?:(?<ChecksumDelim>-)(?<Checksum>\d{2}))?|(?<AccountName>(a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|!|@|#|\$|%|\^|&|\*|\\\(|\\\)|-|\+|\\\{|\\\}|\\\[|\\]|_|\\:|\\"|`|\||\\<|\\>|,|\.|\?|/|~)(a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|0|1|2|3|4|5|6|7|8|9|!|@|#|\$|%|\^|&|\*|\\\(|\\\)|-|\+|\\\{|\\\}|\\\[|\\]|_|\\:|\\"|`|\||\\<|\\>|,|\.|\?|/|~)*))(?:(?<PayloadStartChar>[\[\(<\{])(?<PayloadContent>"( |!|\\"|#|\$|%|&|'|\\\(|\\\)|\*|\+|,|-|\.|/|0|1|2|3|4|5|6|7|8|9|\\:|;|\\<|=|\\>|\?|@|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|\\\[|\\\\|\\]|\^|_|`|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|\\\{|\||\\\}|~)+"|0x(?:[0-9a-f]{2})+|[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)?(?:(?<PayloadPasswordDelim>:){1}(?<PayloadPassword>( |!|\\"|#|\$|%|&|'|\\\(|\\\)|\*|\+|,|-|\.|/|0|1|2|3|4|5|6|7|8|9|\\:|;|\\<|=|\\>|\?|@|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|\\\[|\\\\|\\]|\^|_|`|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|\\\{|\||\\\}|~)+)?)?(?<PayloadEndChar>[]\)>\}]))?(?:(?<ExtendedChecksumDelim>:)(?<ExtendedChecksum>[0-9a-f]{2}[0-9a-f]{2}))?
+((?<AccountNumber>(0|[1-9]\d*))(?:(?<ChecksumDelim>-)(?<Checksum>\d{2}))?|(?<AccountName>(a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|!|@|#|\$|%|\^|&|\*|\\\(|\\\)|-|\+|\\\{|\\\}|\\\[|\\]|_|\\:|\\"|`|\||\\<|\\>|,|\.|\?|/|~)(a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|0|1|2|3|4|5|6|7|8|9|!|@|#|\$|%|\^|&|\*|\\\(|\\\)|-|\+|\\\{|\\\}|\\\[|\\]|_|\\:|\\"|`|\||\\<|\\>|,|\.|\?|/|~)*))(?:(?<PayloadStartChar>[\[\(<\{])(?<PayloadContent>"( |!|\\"|#|\$|%|&|'|\\\(|\\\)|\*|\+|,|-|\.|/|0|1|2|3|4|5|6|7|8|9|\\:|;|\\<|=|\\>|\?|@|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|\\\[|\\\\|\\]|\^|_|`|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|\\\{|\||\\\}|~)+"|0x(?:[0-9a-f]{2})*|[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)?(?:(?<PayloadPasswordDelim>:){1}(?<PayloadPassword>( |!|\\"|#|\$|%|&|'|\\\(|\\\)|\*|\+|,|-|\.|/|0|1|2|3|4|5|6|7|8|9|\\:|;|\\<|=|\\>|\?|@|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|\\\[|\\\\|\\]|\^|_|`|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|\\\{|\||\\\}|~)+)?)?(?<PayloadEndChar>[]\)>\}]))?(?:(?<ExtendedChecksumDelim>:)(?<ExtendedChecksum>[0-9a-f]{2}[0-9a-f]{2}))?
 ```
 
 After matching with the above regex, the named groups need to be extracted and validated as the below snippet shows
@@ -638,4 +638,4 @@ A recursive-descent implementation can be found [here][2].
 
 [1]: https://github.com/Sphere10/NPascalCoin/blob/master/src/NPascalCoin/Common/Text/RegexEPasaParser.cs
 [2]: https://github.com/Sphere10/NPascalCoin/blob/master/src/NPascalCoin/Common/Text/RecursiveDescentEPasaParser.cs
- 
+

+ 8 - 3
PIP/PIP-0033.md

@@ -103,7 +103,8 @@ Array<Operation> senddata({
 - `data_sequence [UINT16]`    
   The data sequence of the operation. If the data sequence cannot be determined (null or not given), it is 0 by default.
 - `guid [GUID]`    
-  A 16 Bytes GUID in `8-4-4-4-12` format. If null or not given, the node will generate a UUID V4 (random).    
+  A 16 Bytes GUID in `{8-4-4-4-12}` format. If null or not given, the node will generate a UUID V4 (random).    
+  Note that in Pascal programming language, GUID is wrapped in {} brackets. Example: {E7213C38-1A97-4CCA-A200-1FF094639BF5}. Brackets {} do not affect the blockchain, but {} should be part of GUID passed to JSON RPC API calls.
 - `amount [PASCCURRENCY] (defult = 0)` 
   The amount to transfer to `target`, if not given or null the default value is 0.       
 
@@ -158,7 +159,8 @@ Array<Operation> signdata({
  - `data_sequence [UINT16]`    
     The data sequence of the operation. If the data sequence cannot be determined (null or not given), it is 0 by default.    
  - `guid [GUID]`    
-    A 16 Bytes GUID in `8-4-4-4-12` format. If null or not given, the node will generate a UUID V4 (random).    
+    A 16 Bytes GUID in `{8-4-4-4-12}` format. If null or not given, the node will generate a UUID V4 (random).    
+	Note that in Pascal programming language GUID is wrapped in {} brackets. Example: {E7213C38-1A97-4CCA-A200-1FF094639BF5}. Brackets {} do not affect the blockchain, but {} should be part of GUID passed to JSON RPC API calls.
  - `last_n_operation UINT32`
     Last value of `n_operation` of the signer (or sender or target)     
  - `amount [PASCCURRENCY] (defult = 0)` 
@@ -198,7 +200,8 @@ Array<Operation> finddataoperations({
 - `target [UINT32] (default = null)`    
   The account that received the DATA operation. Optional.
 - `guid [GUID] (default = null)`    
-  A 16 Bytes GUID in `8-4-4-4-12` format. Optional.
+  A 16 Bytes GUID in `{8-4-4-4-12}` format. Optional.
+  Note that in Pascal programming language GUID is wrapped in {} brackets. Example: {E7213C38-1A97-4CCA-A200-1FF094639BF5}. Brackets {} do not affect the blockchain, but {} should be part of GUID passed to JSON RPC API calls.
 - `data_sequence [UINT16]`    
   The data sequence of the operation to search for. Optional.
 - `data_type [UINT16]`    
@@ -229,3 +232,5 @@ None.
 - UUID V4: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
 
 - PascalCoin JSON RPC documentation: https://github.com/PascalCoin/PascalCoin/wiki/JSON-RPC-API
+
+- DATA-OP: In-Protocol Data Exchange (Layer-2 support) https://www.pascalcoin.org/development/pips/pip-0016

+ 2 - 2
src/config.inc

@@ -68,11 +68,11 @@
   {$DEFINE USE_ABSTRACTMEM}
 
   // Activate GNUGETTEXT library
-  {$DEFINE USE_GNUGETTEXT}
+  {.$DEFINE USE_GNUGETTEXT}
   
   // Activate usage of TPCTemporalFileStream instead of TBytes in order to minimize mem usage
   // This also fixes issue #207 High memory usage on FreePascal compiler
-  {$DEFINE USE_BIGBLOCKS_MEM_ON_DISK}
+  {.$DEFINE USE_BIGBLOCKS_MEM_ON_DISK}
 
 { ********************************************************************
   Don't touch more code, it will addapt based on your preferences

+ 106 - 41
src/core/UAccounts.pas

@@ -30,7 +30,9 @@ uses
   UPCHardcodedRandomHashTable, UJSONFunctions,
   {$IFDEF USE_ABSTRACTMEM}
   UPCAbstractMem, UPCAbstractMemAccountKeys,
+  {$ELSE}
   {$ENDIF}
+  UPCAccountsOrdenations,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 Type
@@ -216,6 +218,8 @@ Type
     FOrderedByName : TOrderedRawList;
     // OrderedAccountKeysList (Added after Build 3.0.1) allows an indexed search of public keys in the safebox with mem optimization
     FOrderedAccountKeysList : TSafeboxPubKeysAndAccounts;
+    FAccountsOrderedByUpdatedBlock : TAccountsOrderedByUpdatedBlock;
+    FAccountsOrderedBySalePrice : TAccountsOrderedBySalePrice;
     {$ENDIF}
     FModifiedBlocksSeparatedChain : TOrderedBlockAccountList; // Used when has PreviousSafebox (Used if we are on a Separated chain)
     //
@@ -245,6 +249,7 @@ Type
     procedure SearchBlockWhenOnSeparatedChain(blockNumber : Cardinal; out blockAccount : TBlockAccount);
     function GetAggregatedHashrate: TBigNum;
     function GetOrderedAccountKeysList: TSafeboxPubKeysAndAccounts;
+    function GetAccount(AAccountNumber : Integer; var AAccount : TAccount) : Boolean;
   protected
     FTotalFee: Int64;
     Procedure UpdateAccount(account_number : Cardinal; const newAccountInfo: TAccountInfo; const newName : TRawBytes; newType : Word;
@@ -290,7 +295,7 @@ Type
     Function FindAccountByName(const aName : String) : Integer; overload;
     Function FindAccountByName(const aName : TRawBytes) : Integer; overload;
     Function FindAccountsStartingByName(const AStartName : TRawBytes; const ARawList : TOrderedRawList; const AMax : Integer = 0) : Integer;
-
+    
     Procedure Clear;
     Function Account(account_number : Cardinal) : TAccount;
     Function GetBlock(block_number : Cardinal) : TBlockAccount;
@@ -322,6 +327,8 @@ Type
     class Function CopyAbstractMemToSafeBoxStream(ASource : TPCAbstractMem; ADestStream : TStream; AFromBlock, AToBlock : Cardinal; var AErrors : String) : Boolean;
     property PCAbstractMem : TPCAbstractMem read FPCAbstractMem;
     {$ENDIF}
+    Function AccountsOrderedByUpdatedBlock : TAccountsOrderedByUpdatedBlock;
+    Function AccountsOrderedBySalePrice : TAccountsOrderedBySalePrice;
   End;
 
 
@@ -494,7 +501,6 @@ uses
   // This issue is not detected on current Delphi memory manager (Tested on Delphi 10.3.2)
 {$ENDIF}
 
-
 { This function is for testing purpose only.
   Will check if Account Names are well assigned and stored }
 function Check_Safebox_Names_Consistency(sb : TPCSafeBox; const title :String; var errors : String) : Boolean;
@@ -2408,6 +2414,24 @@ begin
   end;
 end;
 
+function TPCSafeBox.AccountsOrderedBySalePrice: TAccountsOrderedBySalePrice;
+begin
+  {$IFDEF USE_ABSTRACTMEM}
+  Result := FPCAbstractMem.AccountsOrderedBySalePrice;
+  {$ELSE}
+  Result := FAccountsOrderedBySalePrice;
+  {$ENDIF}
+end;
+
+function TPCSafeBox.AccountsOrderedByUpdatedBlock: TAccountsOrderedByUpdatedBlock;
+begin
+  {$IFDEF USE_ABSTRACTMEM}
+  Result := FPCAbstractMem.AccountsOrderedByUpdatedBlock;
+  {$ELSE}
+  Result := FAccountsOrderedByUpdatedBlock;
+  {$ENDIF}
+end;
+
 function TPCSafeBox.GetBlock(block_number: Cardinal): TBlockAccount;
 begin
   StartThreadSafe;
@@ -2823,6 +2847,8 @@ begin
   FBlockAccountsList := TList<Pointer>.Create;
   FAggregatedHashrate := TBigNum.Create(0);
   FOrderedByName := TOrderedRawList.Create;
+  FAccountsOrderedByUpdatedBlock := TAccountsOrderedByUpdatedBlock.Create(GetAccount);
+  FAccountsOrderedBySalePrice := TAccountsOrderedBySalePrice.Create(GetAccount);
   {$ENDIF}
   FListOfOrderedAccountKeysList := TList<TOrderedAccountKeysList>.Create;
   FCurrentProtocol := CT_PROTOCOL_1;
@@ -2866,6 +2892,11 @@ begin
   FreeAndNil(FAddedNamesSincePreviousSafebox);
   FreeAndNil(FDeletedNamesSincePreviousSafebox);
   FreeAndNil(FSubChains);
+  {$IFnDEF USE_ABSTRACTMEM}
+  FreeAndNil(FAccountsOrderedByUpdatedBlock);
+  FreeAndNil(FAccountsOrderedBySalePrice);
+  {$ENDIF}
+
   If Assigned(FPreviousSafeBox) then begin
     FPreviousSafeBox.FSubChains.Remove(Self); // Remove from current snapshot
     FPreviousSafeBox := Nil;
@@ -2968,7 +2999,7 @@ procedure TPCSafeBox.CommitToPrevious;
     // Start deleting:
     For i:=0 to DeletedNamesList.Count-1 do begin
       {$IFDEF USE_ABSTRACTMEM}
-      FPreviousSafebox.FPCAbstractMem.AccountsNames.Remove(DeletedNamesList.Get(i).ToString);
+      FPreviousSafebox.FPCAbstractMem.AccountsNames.DeleteAccountName(DeletedNamesList.Get(i).ToString);
       {$ELSE}
       FPreviousSafebox.FOrderedByName.Remove(DeletedNamesList.Get(i));
       {$ENDIF}
@@ -2976,7 +3007,7 @@ procedure TPCSafeBox.CommitToPrevious;
     // Finally adding
     For i:=0 to AddedNamesList.Count-1 do begin
       {$IFDEF USE_ABSTRACTMEM}
-      FPreviousSafebox.FPCAbstractMem.AccountsNames.Add(AddedNamesList.Get(i).ToString,AddedNamesList.GetTag(i));
+      FPreviousSafebox.FPCAbstractMem.AccountsNames.AddNameAndNumber(AddedNamesList.Get(i).ToString,AddedNamesList.GetTag(i));
       {$ELSE}
       FPreviousSafebox.FOrderedByName.Add(AddedNamesList.Get(i),AddedNamesList.GetTag(i));
       {$ENDIF}
@@ -3111,19 +3142,23 @@ procedure TPCSafeBox.RollBackToSnapshot(snapshotBlock: Cardinal);
 
    Procedure UndoAddedDeletedNames(AddedNamesList,DeletedNamesList : TOrderedRawList);
    Var i,j : Integer;
+     {$IFDEF USE_ABSTRACTMEM}
+     Laninfo : TAccountNameInfo;
+     {$ELSE}
+     {$ENDIF}
    Begin
      // Start adding
      For i:=0 to AddedNamesList.Count-1 do begin
        // It was added, so we MUST FIND on current names list
        {$IFDEF USE_ABSTRACTMEM}
-       If Not FPCAbstractMem.AccountsNames.FindByName(AddedNamesList.Get(i).ToString,j) then begin
+       If Not FPCAbstractMem.AccountsNames.FindByName(AddedNamesList.Get(i).ToString,Laninfo) then begin
          // ERROR: It has been added, why we not found???
          If DeletedNamesList.Find(AddedNamesList.Get(i),j) then begin
          end else begin
            TLog.NewLog(lterror,ClassName,Format('ERROR DEV 20180319-1 Name %s not found at account:%d',[AddedNamesList.Get(i).ToPrintable,AddedNamesList.GetTag(i)]));
          end;
        end else begin
-         FPCAbstractMem.AccountsNames.Delete(j);
+         FPCAbstractMem.AccountsNames.DeleteData(Laninfo);
        end;
        {$ELSE}
        If Not FOrderedByName.Find(AddedNamesList.Get(i),j) then begin
@@ -3139,15 +3174,14 @@ procedure TPCSafeBox.RollBackToSnapshot(snapshotBlock: Cardinal);
      For i:=0 to DeletedNamesList.Count-1 do begin
        {$IFDEF USE_ABSTRACTMEM}
        // It has been deleted, we MUST NOT FIND on current names list
-       If FPCAbstractMem.AccountsNames.FindByName(DeletedNamesList.Get(i).ToString,j) then begin
-         // It has been deleted... now is found
-         If (FPCAbstractMem.AccountsNames.Item[j].accountNumber<>DeletedNamesList.GetTag(i)) then begin
+       If FPCAbstractMem.AccountsNames.FindByName(DeletedNamesList.Get(i).ToString,Laninfo) then begin
+         if Laninfo.accountNumber<>DeletedNamesList.GetTag(i) then begin
            // ERROR: It has been deleted, why is found with another account???
-           TLog.NewLog(lterror,ClassName,Format('ERROR DEV 20180319-2 Name %s found at account:%d <> saved account:%d',[DeletedNamesList.Get(i).ToPrintable,DeletedNamesList.GetTag(i),FPCAbstractMem.AccountsNames.Item[j].accountNumber]));
+           TLog.NewLog(lterror,ClassName,Format('ERROR DEV 20180319-2 Name %s found at account:%d <> saved account:%d',[DeletedNamesList.Get(i).ToPrintable,DeletedNamesList.GetTag(i),Laninfo.accountNumber]));
          end;
        end;
        // Add with Info of previous account with name (saved at Tag value)
-       FPCAbstractMem.AccountsNames.Add(DeletedNamesList.Get(i).ToString,DeletedNamesList.GetTag(i));
+       FPCAbstractMem.AccountsNames.AddNameAndNumber(DeletedNamesList.Get(i).ToString,DeletedNamesList.GetTag(i));
        {$ELSE}
        // It has been deleted, we MUST NOT FIND on current names list
        If FOrderedByName.Find(DeletedNamesList.Get(i),j) then begin
@@ -3412,7 +3446,7 @@ begin
       if sbHeader.blocksCount<FPCAbstractMem.BlocksCount then begin
         FPCAbstractMem.EraseData;
       end else begin
-        FPCAbstractMem.AccountsNames.Clear;
+        FPCAbstractMem.AccountsNames.EraseTree;
       end;
       AggregatedHashrate.Value := 0;
       {$ELSE}
@@ -3514,11 +3548,11 @@ begin
               Exit;
             end;
             {$IFDEF USE_ABSTRACTMEM}
-            if FPCAbstractMem.AccountsNames.IndexOf( LBlock.accounts[iacc].name.ToString )>=0 then begin
+            if FPCAbstractMem.AccountsNames.FindByName(LBlock.accounts[iacc].name.ToString ) then begin
               errors := errors + ' Duplicate name "'+LBlock.accounts[iacc].name.ToPrintable+'"';
               Exit;
             end;
-            FPCAbstractMem.AccountsNames.Add(LBlock.accounts[iacc].name.ToString,LBlock.accounts[iacc].account);
+            FPCAbstractMem.AccountsNames.AddNameAndNumber(LBlock.accounts[iacc].name.ToString,LBlock.accounts[iacc].account);
             {$ELSE}
             if FOrderedByName.IndexOf(LBlock.accounts[iacc].name)>=0 then begin
               errors := errors + ' Duplicate name "'+LBlock.accounts[iacc].name.ToPrintable+'"';
@@ -3555,9 +3589,9 @@ begin
             if ((iblock + (CT_BankToDiskEveryNBlocks * 10)) >= sbHeader.blockscount) then begin
             {$ENDIF}
               {$IFDEF ASSUME_VALID_POW_OLD_PROTOCOLS}
-              LAddToMultiThreadOperationsBlockValidator := (LUseMultiThreadOperationsBlockValidator) and (LBlock.blockchainInfo.protocol_version>=CT_PROTOCOL_5) and (Assigned(LPCOperationsBlockValidator));
+              LAddToMultiThreadOperationsBlockValidator := False;
               {$ELSE}
-              LAddToMultiThreadOperationsBlockValidator := (LUseMultiThreadOperationsBlockValidator) and (LBlock.blockchainInfo.protocol_version>=CT_PROTOCOL_4) and (Assigned(LPCOperationsBlockValidator));
+              LAddToMultiThreadOperationsBlockValidator := (LUseMultiThreadOperationsBlockValidator) and (LBlock.blockchainInfo.protocol_version=CT_PROTOCOL_4) and (Assigned(LPCOperationsBlockValidator));
               {$ENDIF}
               If not IsValidNewOperationsBlock(LBlock.blockchainInfo,False,Not LAddToMultiThreadOperationsBlockValidator,aux_errors) then begin
                 errors := errors + ' > ' + aux_errors;
@@ -3603,6 +3637,7 @@ begin
             Exit;
           end;
         end;
+
         // Add
         {$IFDEF USE_ABSTRACTMEM}
         FPCAbstractMem.SetBlockAccount(LBlock);
@@ -3613,6 +3648,17 @@ begin
         // BufferBlocksHash fill with data
         j := (length(LBlock.block_hash)*(iBlock));
         BufferBlocksHash.Replace( j, LBlock.block_hash[0], 32 );
+        for j := low(LBlock.accounts) to High(LBlock.accounts) do begin
+          FAccountsOrderedByUpdatedBlock.Update(
+            LBlock.accounts[j].account,
+            0,
+            LBlock.accounts[j].updated_on_block_active_mode);
+          FAccountsOrderedBySalePrice.UpdateAccountBySalePrice(
+            LBlock.accounts[j].account,
+            CT_AccountInfo_NUL,
+            LBlock.accounts[j].accountInfo
+            );
+        end;
         {$ENDIF}
         for j := low(LBlock.accounts) to High(LBlock.accounts) do begin
           AccountKeyListAddAccounts(LBlock.accounts[j].accountInfo.accountKey,[LBlock.accounts[j].account]);
@@ -3636,7 +3682,6 @@ begin
         // Assign to previous
         LPreviousProofOfWork := LBlock.blockchainInfo.proof_of_work;
       end; // For iBlock ...
-
         if Assigned(LPCOperationsBlockValidator) then begin
           repeat
             LPCOperationsBlockValidator.GetStatus(LValidatedOPOk, LValidatedOPError, LValidatedOPPending);
@@ -4536,6 +4581,12 @@ begin
   end;
 end;
 
+function TPCSafeBox.GetAccount(AAccountNumber: Integer; var AAccount: TAccount): Boolean;
+begin
+  AAccount := Account(AAccountNumber);
+  Result := True;
+end;
+
 function TPCSafeBox.GetActualCompactTargetHash(protocolVersion : Word): Cardinal;
 begin
   Result := TPascalCoinProtocol.TargetToCompact(GetActualTargetHash(protocolVersion),protocolVersion);
@@ -4549,10 +4600,13 @@ end;
 function TPCSafeBox.FindAccountByName(const aName: TRawBytes): Integer;
 Var i,j,k : Integer;
   Psnapshot : PSafeboxSnapshot;
+  {$IFDEF USE_ABSTRACTMEM}
+  Laninfo : TAccountNameInfo;
+  {$ENDIF}
 begin
   {$IFDEF USE_ABSTRACTMEM}
-  i := FPCAbstractMem.AccountsNames.IndexOf(aName.ToString);
-  if i>=0 then Result := FPCAbstractMem.AccountsNames.Item[i].accountNumber
+  if FPCAbstractMem.AccountsNames.FindByName(aName.ToString,Laninfo) then
+    Result := Laninfo.accountNumber
   {$ELSE}
   i := FOrderedByName.IndexOf(aName);
   if i>=0 then Result := FOrderedByName.GetTag(i)
@@ -4607,26 +4661,24 @@ end;
 
 function TPCSafeBox.FindAccountsStartingByName(const AStartName: TRawBytes;
   const ARawList: TOrderedRawList; const AMax: Integer = 0): Integer;
-var LIndex : Integer;
+var
   LRaw : TRawBytes;
-  LStartNameString : String;
+  {$IFDEF USE_ABSTRACTMEM}
+  Laninfo : TAccountNameInfo;
+  {$ELSE}
+  LIndex : Integer;
+  {$ENDIF}
 begin
   ARawList.Clear;
   StartThreadSafe;
   try
     {$IFDEF USE_ABSTRACTMEM}
-    if FPCAbstractMem.AccountsNames.FindByName(AStartName.ToString,LIndex) then begin
-      LRaw.FromString(FPCAbstractMem.AccountsNames.Item[LIndex].accountName);
-      ARawList.Add( LRaw, FPCAbstractMem.AccountsNames.Item[LIndex].accountNumber );
-      inc(LIndex);
-    end;
-    LStartNameString := AStartName.ToString;
-    while (LIndex<FPCAbstractMem.AccountsNames.Count) and (FPCAbstractMem.AccountsNames.Item[LIndex].accountName.StartsWith( LStartNameString ) )
-      and ((AMax<=0) or (AMax>ARawList.Count)) // AMax <=0 inifinte results
-      do begin
-      LRaw.FromString( FPCAbstractMem.AccountsNames.Item[LIndex].accountName );
-      ARawList.Add( LRaw, FPCAbstractMem.AccountsNames.Item[LIndex].accountNumber );
-      inc(LIndex);
+    FPCAbstractMem.AccountsNames.FindByName(AStartName.ToString,Laninfo);
+    while (Laninfo.accountName.StartsWith(AStartName.ToString))
+      and ((AMax<=0) or (AMax>ARawList.Count)) do begin
+      LRaw.FromString(Laninfo.accountName);
+      ARawList.Add( LRaw, Laninfo.accountNumber );
+      if not FPCAbstractMem.AccountsNames.FindDataSuccessor(Laninfo,Laninfo) then Break;
     end;
     {$ELSE}
     if FOrderedByName.Find(AStartName,LIndex) then begin
@@ -4703,6 +4755,8 @@ Var iBlock : Cardinal;
   blockAccount : TBlockAccount;
   {$IFnDEF USE_ABSTRACTMEM}
   Pblock : PBlockAccount;
+  {$ELSE}
+  Laninfo : TAccountNameInfo;
   {$ENDIF}
 begin
   iBlock := account_number DIV CT_AccountsPerBlock;
@@ -4716,6 +4770,16 @@ begin
   end else begin
     Pblock := FBlockAccountsList.Items[iBlock];
   end;
+  FAccountsOrderedByUpdatedBlock.Update(
+    account_number,
+    blockAccount.accounts[iAccount].updated_on_block_active_mode,
+    newUpdated_block_active_mode
+   );
+  FAccountsOrderedBySalePrice.UpdateAccountBySalePrice(
+    account_number,
+    blockAccount.accounts[iAccount].accountInfo,
+    newAccountInfo
+   );
   {$ENDIF}
 
   if (NOT TAccountComp.EqualAccountKeys(blockAccount.accounts[iAccount].accountInfo.accountKey,newAccountInfo.accountKey)) then begin
@@ -4746,16 +4810,15 @@ begin
       If Length(blockAccount.accounts[iAccount].name)>0 then begin
 
         {$IFDEF USE_ABSTRACTMEM}
-        i := FPCAbstractMem.AccountsNames.IndexOf(blockAccount.accounts[iAccount].name.ToString);
-        if i<0 then begin
+        if Not FPCAbstractMem.AccountsNames.FindByName(blockAccount.accounts[iAccount].name.ToString,Laninfo) then begin
           If (Not Assigned(FPreviousSafeBox)) then begin
             TLog.NewLog(ltError,ClassName,'ERROR DEV 20170606-1 Name "'+blockAccount.accounts[iAccount].name.ToPrintable+'" not found for delete on account '+IntToStr(account_number));
           end;
         end else begin
-          If (FPCAbstractMem.AccountsNames.Item[i].accountNumber<>account_number) then begin
-            TLog.NewLog(ltError,ClassName,'ERROR DEV 20170606-3 Name "'+blockAccount.accounts[iAccount].name.ToPrintable+'" not found for delete at suposed account '+IntToStr(account_number)+' found at '+IntToStr(FPCAbstractMem.AccountsNames.Item[i].accountNumber)+' '+FPCAbstractMem.AccountsNames.Item[i].accountName);
+          If (Laninfo.accountNumber<>account_number) then begin
+            TLog.NewLog(ltError,ClassName,'ERROR DEV 20170606-3 Name "'+blockAccount.accounts[iAccount].name.ToPrintable+'" not found for delete at suposed account '+IntToStr(account_number)+' found at '+IntToStr(Laninfo.accountNumber)+' '+Laninfo.accountName);
           end;
-          FPCAbstractMem.AccountsNames.Delete(i);
+          FPCAbstractMem.AccountsNames.DeleteData(Laninfo);
         end;
         {$ELSE}
         i := FOrderedByName.IndexOf(blockAccount.accounts[iAccount].name);
@@ -4793,9 +4856,11 @@ begin
       blockAccount.accounts[iAccount].name:=newName;
       If Length(blockAccount.accounts[iAccount].name)>0 then begin
         {$IFDEF USE_ABSTRACTMEM}
-        i := FPCAbstractMem.AccountsNames.IndexOf(blockAccount.accounts[iAccount].name.ToString);
-        if i>=0 then TLog.NewLog(ltError,ClassName,'ERROR DEV 20170606-2 New Name "'+blockAccount.accounts[iAccount].name.ToPrintable+'" for account '+IntToStr(account_number)+' found at account '+IntToStr(FPCAbstractMem.AccountsNames.Item[i].accountNumber));
-        FPCAbstractMem.AccountsNames.Add(blockAccount.accounts[iAccount].name.ToString,account_number);
+        if FPCAbstractMem.AccountsNames.FindByName(blockAccount.accounts[iAccount].name.ToString,Laninfo) then begin
+          TLog.NewLog(ltError,ClassName,'ERROR DEV 20170606-2 New Name "'+blockAccount.accounts[iAccount].name.ToPrintable+'" for account '+IntToStr(account_number)+' found at account '+IntToStr(Laninfo.accountNumber));
+          FPCAbstractMem.AccountsNames.DeleteData(Laninfo);
+        end;
+        FPCAbstractMem.AccountsNames.AddNameAndNumber(blockAccount.accounts[iAccount].name.ToString,account_number);
         {$ELSE}
         i := FOrderedByName.IndexOf(blockAccount.accounts[iAccount].name);
         if i>=0 then TLog.NewLog(ltError,ClassName,'ERROR DEV 20170606-2 New Name "'+blockAccount.accounts[iAccount].name.ToPrintable+'" for account '+IntToStr(account_number)+' found at account '+IntToStr(FOrderedByName.GetTag(i)));

+ 5 - 2
src/core/UBlockChain.pas

@@ -188,6 +188,7 @@ Type
     Balance : Int64;
     OriginalPayload : TOperationPayload;
     PrintablePayload : String;
+    DecodedEPasaPayload : String;
     OperationHash : TRawBytes;
     OperationHash_OLD : TRawBytes; // Will include old oeration hash value
     errors : String;
@@ -574,7 +575,7 @@ Type
 
 Const
   CT_TOperationPayload_NUL : TOperationPayload = (payload_type:0;payload_raw:Nil);
-  CT_TOperationResume_NUL : TOperationResume = (valid:false;Block:0;NOpInsideBlock:-1;OpType:0;OpSubtype:0;time:0;AffectedAccount:0;SignerAccount:-1;n_operation:0;DestAccount:-1;SellerAccount:-1;newKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);OperationTxt:'';Amount:0;Fee:0;Balance:0;OriginalPayload:(payload_type:0;payload_raw:nil);PrintablePayload:'';OperationHash:Nil;OperationHash_OLD:Nil;errors:'';isMultiOperation:False;Senders:Nil;Receivers:Nil;changers:Nil);
+  CT_TOperationResume_NUL : TOperationResume = (valid:false;Block:0;NOpInsideBlock:-1;OpType:0;OpSubtype:0;time:0;AffectedAccount:0;SignerAccount:-1;n_operation:0;DestAccount:-1;SellerAccount:-1;newKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);OperationTxt:'';Amount:0;Fee:0;Balance:0;OriginalPayload:(payload_type:0;payload_raw:nil);PrintablePayload:'';DecodedEPasaPayload:'';OperationHash:Nil;OperationHash_OLD:Nil;errors:'';isMultiOperation:False;Senders:Nil;Receivers:Nil;changers:Nil);
   CT_TMultiOpSender_NUL : TMultiOpSender =  (Account:0;Amount:0;N_Operation:0;Payload:(payload_type:0;payload_raw:Nil);Signature:(r:Nil;s:Nil));
   CT_TMultiOpReceiver_NUL : TMultiOpReceiver = (Account:0;Amount:0;Payload:(payload_type:0;payload_raw:Nil));
   CT_TMultiOpChangeInfo_NUL : TMultiOpChangeInfo = (Account:0;N_Operation:0;Changes_type:[];New_Accountkey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);New_Name:Nil;New_Type:0;New_Data:Nil;Seller_Account:-1;Account_Price:-1;Locked_Until_Block:0;
@@ -3816,7 +3817,9 @@ begin
   l := FList.LockList;
   try
     if index<l.Count then Result := POperationResume(l[index])^
-    else Result := CT_TOperationResume_NUL;
+    else begin
+      Result := CT_TOperationResume_NUL;
+    end;
   finally
     FList.UnlockList;
   end;

+ 11 - 5
src/core/UConst.pas

@@ -130,9 +130,9 @@ Const
   CT_Protocol_Upgrade_v6_MinBlock = {$IFDEF PRODUCTION}999999999{$ELSE}999999999{$ENDIF}; // TODO: ALLOW V6 activate setting a valid "min block" value
 
 
-  CT_MagicNetIdentification = {$IFDEF PRODUCTION}$0A043580{$ELSE}$05000004{$ENDIF};
+  CT_MagicNetIdentification = {$IFDEF PRODUCTION}$0A043580{$ELSE}$05000005{$ENDIF};
 
-  CT_NetProtocol_Version: Word = 10;
+  CT_NetProtocol_Version: Word = 12;
   // IMPORTANT NOTE!!!
   // NetProtocol_Available MUST BE always >= NetProtocol_version
   CT_NetProtocol_Available: Word = {$IFDEF PRODUCTION}12{$ELSE}12{$ENDIF};
@@ -141,9 +141,9 @@ Const
 
   CT_SafeBoxBankVersion : Word = 3; // Protocol 2 upgraded safebox version from 2 to 3
 
-  CT_MagicIdentificator: String = {$IFDEF PRODUCTION}'PascalCoin'{$ELSE}'PascalCoinTESTNET_5.Beta.4'{$ENDIF}; //
+  CT_MagicIdentificator: String = {$IFDEF PRODUCTION}'PascalCoin'{$ELSE}'PascalCoin_TESTNET'{$ENDIF}; //
 
-  CT_PascalCoin_Data_Folder : String = {$IFDEF PRODUCTION}'PascalCoin'{$ELSE}'PascalCoin_TESTNET_5.Beta.4'{$ENDIF}; //
+  CT_PascalCoin_Data_Folder : String = {$IFDEF PRODUCTION}'PascalCoin'{$ELSE}'PascalCoin_TESTNET'{$ENDIF}; //
 
   CT_PseudoOp_Reward = $0;
   // Value of Operations type in Protocol 1
@@ -198,7 +198,7 @@ Const
   CT_OpSubtype_Data_Signer                = 103;
   CT_OpSubtype_Data_Receiver              = 104;
 
-  CT_ClientAppVersion : String = {$IFDEF PRODUCTION}'5.4.Beta'{$ELSE}{$IFDEF TESTNET}'TESTNET 5.4.beta'{$ELSE}{$ENDIF}{$ENDIF};
+  CT_ClientAppVersion : String = {$IFDEF PRODUCTION}'5.4'{$ELSE}{$IFDEF TESTNET}'TESTNET 5.4'{$ELSE}{$ENDIF}{$ENDIF};
 
   CT_Discover_IPs = {$IFDEF PRODUCTION}'bpascal1.dynamic-dns.net;bpascal2.dynamic-dns.net;pascalcoin1.dynamic-dns.net;pascalcoin2.dynamic-dns.net;pascalcoin1.dns1.us;pascalcoin2.dns1.us;pascalcoin1.dns2.us;pascalcoin2.dns2.us'
                     {$ELSE}'pascaltestnet1.dynamic-dns.net;pascaltestnet2.dynamic-dns.net;pascaltestnet1.dns1.us;pascaltestnet2.dns1.us'{$ENDIF};
@@ -217,8 +217,14 @@ Const
   CT_MOLINA  = 1;
   CT_MOLINA_DECIMAL = {$IFDEF FPC}Real(CT_MOLINA/1000.0);{$ELSE}0.0001;{$ENDIF}
 
+  CT_DEFAULT_PAY_TO_KEY_MAX_MOLINAS = 5000;
+
   CT_ACTIVATE_RANDOMHASH_V4 = {$IFDEF ACTIVATE_RANDOMHASH_V4}True{$ELSE}False{$ENDIF};
 
+  // Represents a non-existent account number
+  // (chosen as the last account in safebox, generated in year 6101)
+  CT_AccountNo_NUL = High(Cardinal);
+
   // App Params
   CT_PARAM_GridAccountsStream = 'GridAccountsStreamV2';
   CT_PARAM_GridAccountsPos = 'GridAccountsPos';

+ 292 - 117
src/core/UEPasa.pas

@@ -27,10 +27,10 @@ uses
   SysUtils,
   TypInfo,
   uregexpr,
-  UAccounts,
   UCommon,
   UCrypto,
-  UEncoding;
+  UEncoding,
+  SyncObjs;
 
 type
 
@@ -43,89 +43,106 @@ type
     BadExtendedChecksum
   );
 
-  { PayloadType}
-
-  PayloadType = (
-    NonDeterministic = 0,      // Payload encryption and encoding method not specified.
-    &Public = 1,               // Unencrypted, public payload.
-    RecipientKeyEncrypted = 2, // ECIES encrypted using recipient accounts public key.
-    SenderKeyEncrypted = 3,    // ECIES encrypted using sender accounts public key.
-    PasswordEncrypted = 4,     // AES encrypted using pwd param
-    AsciiFormatted = 5,        // Payload data encoded in ASCII
-    HexFormatted = 6,          // Payload data encoded in HEX
-    Base58Formatted = 7,       // Payload data encoded in Base58
-    AddressedByName = 8        // E-PASA addressed by account name (not number).
+  { TPayloadTrait }
+
+  TPayloadTrait = (
+    ptNonDeterministic = 0,      // Payload encryption and encoding method not specified.
+    ptPublic = 1,                // Unencrypted, public payload.
+    ptRecipientKeyEncrypted = 2, // ECIES encrypted using recipient accounts public key.
+    ptSenderKeyEncrypted = 3,    // ECIES encrypted using sender accounts public key.
+    ptPasswordEncrypted = 4,     // AES encrypted using pwd param
+    ptAsciiFormatted = 5,        // Payload data encoded in ASCII
+    ptHexFormatted = 6,          // Payload data encoded in HEX
+    ptBase58Formatted = 7,       // Payload data encoded in Base58
+    ptAddressedByName = 8        // E-PASA addressed by account name (not number).
   );
 
-  { PayloadTypeHelper }
+  { TPayloadTraitHelper }
 
-  PayloadTypeHelper = record helper for PayloadType
+  TPayloadTraitHelper = record helper for TPayloadTrait
   public
-    function Value: Int32;
+    function ToProtocolValue: byte;
   end;
 
-  { PayloadTypes }
+  { TPayloadType }
 
-  PayloadTypes = set of PayloadType;
+  TPayloadType = set of TPayloadTrait;
 
-  { PayloadTypesHelper }
+  { TPayloadTypesHelper }
 
-  PayloadTypesHelper = record helper for PayloadTypes
+  TPayloadTypeHelper = record helper for TPayloadType
   public
-    function IsPayloadTypeInSet(APayloadType: PayloadType): Boolean; inline;
+    function HasTrait(APayloadTrait: TPayloadTrait): Boolean; inline;
+    function ToProtocolValue : byte;
+    function IsValid : Boolean;
   end;
 
-
   { TEPasa }
 
+
   TEPasa = record
-    strict private
-      var
-        FAccount, FAccountChecksum: TNullable<UInt32>;
-        FAccountName, FPayload, FPassword, FExtendedChecksum: String;
-        FPayloadTypes: PayloadTypes;
+    strict private var
+      FAccount, FAccountChecksum: TNullable<UInt32>;
+      FAccountName, FPayload, FPassword, FExtendedChecksum: String;
+      FPayloadType: TPayloadType;
 
       function GetAccount: TNullable<UInt32>; inline;
       procedure SetAccount(const AValue: TNullable<UInt32>); inline;
       function GetAccountChecksum: TNullable<UInt32>; inline;
       procedure SetAccountChecksum(const AValue: TNullable<UInt32>); inline;
-      function GetPayloadTypes: PayloadTypes; inline;
+      function GetPayloadType: TPayloadType; inline;
       function GetAccountName: String; inline;
       procedure SetAccountName(const AValue: String); inline;
-      procedure SetPayloadTypes(const AValue: PayloadTypes); inline;
+      procedure SetPayloadType(const AValue: TPayloadType); inline;
       function GetExtendedChecksum: String; inline;
       procedure SetExtendedChecksum(const AValue: String); inline;
       function GetPassword: String; inline;
       procedure SetPassword(const AValue: String); inline;
       function GetPayload: String; inline;
       procedure SetPayload(const AValue: String); inline;
-
+      function GetHasPayload: Boolean; inline;
+      function GetIsStandard: Boolean; inline;
+      function GetIsPayToKey: Boolean; inline;
+      function GetIsAddressedByName : Boolean; inline;
+      function GetIsAddressedByNumber : Boolean; inline;
+      class function GetEmptyValue : TEPasa; static;
     public
-      function IsPayToKey: Boolean; inline;
-      function GetRawPayloadBytes(): TArray<Byte>; inline;
-      function ToString(): String; overload;
-      function ToString(AOmitExtendedChecksum: Boolean): String; overload;
-
       property Account: TNullable<UInt32> read GetAccount write SetAccount;
       property AccountChecksum: TNullable<UInt32> read GetAccountChecksum write SetAccountChecksum;
       property AccountName: String read GetAccountName write SetAccountName;
-      property PayloadTypes: PayloadTypes read GetPayloadTypes write SetPayloadTypes;
+      property PayloadType: TPayloadType read GetPayloadType write SetPayloadType;
       property Payload: String read GetPayload write SetPayload;
       property Password: String read GetPassword write SetPassword;
       property ExtendedChecksum: String read GetExtendedChecksum write SetExtendedChecksum;
+      property IsAddressedByNumber: boolean read GetIsAddressedByNumber;
+      property IsAddressedByName: boolean read GetIsAddressedByName;
+      property IsPayToKey: boolean read GetIsPayToKey;
+      property IsClassicPASA: boolean read GetIsStandard;
+      property HasPayload: boolean read GetHasPayload;
+      class property Empty : TEPasa read GetEmptyValue;
+
+      function GetRawPayloadBytes(): TBytes; inline;
+
+      function ToClassicPASAString(): String; overload;
+      function ToString(): String; overload;
+      function ToString(AOmitExtendedChecksum: Boolean): String; overload;
 
-      class function TryParse(const AEPasaText: String; out AEPasa: TEPasa) : Boolean; static;
+      class function TryParse(const AEPasaText: String; AOmitExtendedChecksumVerification : Boolean; out AEPasa: TEPasa) : Boolean; overload; static;
+      class function TryParse(const AEPasaText: String; out AEPasa: TEPasa) : Boolean; overload; static;
       class function Parse(const AEPasaText: String): TEPasa; static;
 
       class function CalculateAccountChecksum(AAccNo: UInt32): Byte; static; inline;
-
+      procedure Clear;
   end;
 
+
+
   { TEPasaParser }
 
   TEPasaParser = class
     strict private
       class var FEPasaRegex: TCustomRegex;
+      class var FEPasaLocker : TCriticalSection;
       class constructor CreateRegexEPasaParser();
       class destructor DestroyRegexEPasaParser();
 
@@ -134,7 +151,7 @@ type
       // note: regex syntax escapes following chars [\^$.|?*+(){}
       // note: epasa syntax escapes following chars: :\"[]()<>(){}
       // note: c-sharp syntax verbatim strings escape: " as ""
-      IntegerPattern = '(0|[1-9]\d+)';
+      IntegerPattern = '(0|[1-9]\d*)';
       AccountNamePattern = '(?P<AccountName>' + TPascal64Encoding.StringPattern + ')';
       AccountChecksumPattern = '(?:(?P<ChecksumDelim>-)(?P<Checksum>\d{2}))?';
       AccountNumberPattern = '(?P<AccountNumber>' + IntegerPattern + ')' + AccountChecksumPattern;
@@ -153,11 +170,12 @@ type
       function Parse(const AEPasaText: String): TEPasa;
       function TryParse(const AEPasaText: String; out AEPasa: TEPasa): Boolean; overload;
       function TryParse(const AEPasaText: String; out AEPasa: TEPasa; out AErrorCode: EPasaErrorCode): Boolean; overload;
+      function TryParse(const AEPasaText: String; AOmitExtendedChecksumVerification : Boolean; out AEPasa: TEPasa; out AErrorCode: EPasaErrorCode): Boolean; overload;
   end;
 
-  { TEPasaHelper }
+  { TEPasaComp }
 
-  TEPasaHelper = class sealed(TObject)
+  TEPasaComp = class sealed(TObject)
 
     strict private
       class function ReadUInt16AsBytesLE(AValue: UInt16): TArray<Byte>; static;
@@ -177,9 +195,12 @@ type
 
       class function ComputeExtendedChecksum(const AText: String): String; static;
       class function IsValidExtendedChecksum(const AText: String; const AChecksum: String): Boolean; static;
-      class function IsValidPayloadLength(APayloadType: PayloadTypes; const APayloadContent: String): Boolean; static;
+      class function IsValidPayloadLength(APayloadType: TPayloadType; const APayloadContent: String): Boolean; static;
       class function IsValidPasswordLength(const APasswordValue: String) : Boolean; static;
 
+      class function GetPayloadTypeProtocolByte(const APayloadType : TPayloadType) : Byte;
+      class function GetPayloadTypeFromProtocolByte(AByte : Byte) : TPayloadType;
+      class function FromProtocolValue(AVal : Byte) : TPayloadType;
   end;
 
 resourcestring
@@ -200,32 +221,79 @@ uses
   HlpConverters,
   UMemory;
 
-{ PayloadTypeHelper }
+var
+  EmptyEPasa : TEPasa;
+
+{ TPayloadTraitHelper }
 
-function PayloadTypeHelper.Value: Int32;
+function TPayloadTraitHelper.ToProtocolValue: Byte;
 begin
   case Self of
-    NonDeterministic: Result := $00000000;
-    &Public: Result := $00000001;
-    RecipientKeyEncrypted: Result := $00000010;
-    SenderKeyEncrypted: Result := $00000100;
-    PasswordEncrypted: Result := $00001000;
-    AsciiFormatted: Result := $00010000;
-    HexFormatted: Result := $00100000;
-    Base58Formatted: Result := $01000000;
-    AddressedByName: Result := $10000000;
+    ptNonDeterministic: Exit(0);
+    ptPublic: Exit(BYTE_BIT_0);
+    ptRecipientKeyEncrypted: Exit(BYTE_BIT_1);
+    ptSenderKeyEncrypted: Exit(BYTE_BIT_2);
+    ptPasswordEncrypted: Exit(BYTE_BIT_3);
+    ptAsciiFormatted: Exit(BYTE_BIT_4);
+    ptHexFormatted: Exit(BYTE_BIT_5);
+    ptBase58Formatted: Exit(BYTE_BIT_6);
+    ptAddressedByName: Exit(BYTE_BIT_7);
   end;
+  raise Exception.Create('Internal Error 2faed11a-1b0f-447a-87d1-2e1735ac4ca2');
 end;
 
-{ PayloadTypesHelper }
+{ TPayloadTypeHelper }
 
-function PayloadTypesHelper.IsPayloadTypeInSet(APayloadType : PayloadType) : Boolean;
+function TPayloadTypeHelper.HasTrait(APayloadTrait : TPayloadTrait) : Boolean;
 begin
-  Result := APayloadType in Self;
+  Result := APayloadTrait in Self;
+end;
+
+function TPayloadTypeHelper.IsValid: Boolean;
+var LValue, LEncryptedBits, LFormattedBits : Byte;
+begin
+  { As described on PIP-0027 E-PASA:
+    Bits 0..3 describe how payload is encrypted. 1 option (and only 1) must be selected
+    Bits 4..6 describe how is data encoded: String, Hexa or Base58. 1 option (and 1 only 1) must be selected
+
+    IsValid = 1 bit from 0..3 and 1 bit from 4..6 must be selected
+  }
+  LValue := Self.ToProtocolValue;
+  LEncryptedBits := (LValue and $0F); // 0000 1111
+  LFormattedBits := (LValue and $70); // 0111 0000
+  Result :=
+      (
+         ((LEncryptedBits and BYTE_BIT_0)=BYTE_BIT_0)
+      or ((LEncryptedBits and BYTE_BIT_1)=BYTE_BIT_1)
+      or ((LEncryptedBits and BYTE_BIT_2)=BYTE_BIT_2)
+      or ((LEncryptedBits and BYTE_BIT_3)=BYTE_BIT_3)
+      )
+      and
+      (
+         ((LFormattedBits and BYTE_BIT_4)=BYTE_BIT_4)
+      or ((LFormattedBits and BYTE_BIT_5)=BYTE_BIT_5)
+      or ((LFormattedBits and BYTE_BIT_6)=BYTE_BIT_6)
+      );
+end;
+
+function TPayloadTypeHelper.ToProtocolValue : Byte;
+begin
+  Result := TEPasaComp.GetPayloadTypeProtocolByte(Self);
 end;
 
 { TEPasa }
 
+procedure TEPasa.Clear;
+begin
+  Self.FAccount.Clear;
+  Self.FAccountChecksum.Clear;
+  Self.FAccountName:='';
+  Self.FPayload:='';
+  Self.FPassword:='';
+  Self.FExtendedChecksum:='';
+  Self.FPayloadType:=[];
+end;
+
 function TEPasa.GetAccount: TNullable<UInt32>;
 begin
   Result := FAccount;
@@ -256,9 +324,9 @@ begin
   Result := FPayload;
 end;
 
-function TEPasa.GetPayloadTypes: PayloadTypes;
+function TEPasa.GetPayloadType: TPayloadType;
 begin
-  Result := FPayloadTypes;
+  Result := FPayloadType;
 end;
 
 procedure TEPasa.SetAccount(const AValue: TNullable<UInt32>);
@@ -291,89 +359,128 @@ begin
   FPayload := AValue;
 end;
 
-procedure TEPasa.SetPayloadTypes(const AValue: PayloadTypes);
+procedure TEPasa.SetPayloadType(const AValue: TPayloadType);
+begin
+  FPayloadType := AValue;
+end;
+
+function TEPasa.GetIsAddressedByNumber : Boolean;
+begin
+  Result := NOT PayloadType.HasTrait(ptAddressedByName);
+end;
+
+function TEPasa.GetIsAddressedByName : Boolean;
 begin
-  FPayloadTypes := AValue;
+  Result := (NOT IsPayToKey) AND PayloadType.HasTrait(ptAddressedByName);
 end;
 
-function TEPasa.IsPayToKey: Boolean;
+function TEPasa.GetIsPayToKey: Boolean;
 begin
   Result :=
     (AccountName = '@') and
-    (PayloadTypes.IsPayloadTypeInSet(PayloadType.AddressedByName) and
-    PayloadTypes.IsPayloadTypeInSet(PayloadType.Public) and
-    PayloadTypes.IsPayloadTypeInSet(PayloadType.Base58Formatted));
+    (PayloadType.HasTrait(ptAddressedByName) and
+    PayloadType.HasTrait(ptPublic) and
+    PayloadType.HasTrait(ptBase58Formatted));
+end;
+
+function TEPasa.GetIsStandard: Boolean;
+begin
+  Result := (NOT PayloadType.HasTrait(ptAddressedByName)) AND (NOT HasPayload);
 end;
 
-function TEPasa.GetRawPayloadBytes: TArray<Byte>;
+function TEPasa.GetHasPayload: Boolean;
 begin
-  if (PayloadTypes.IsPayloadTypeInSet(PayloadType.AsciiFormatted)) then
+  Result := PayloadType.HasTrait(ptPublic) OR PayloadType.HasTrait(ptRecipientKeyEncrypted) OR PayloadType.HasTrait(ptSenderKeyEncrypted);
+end;
+
+function TEPasa.GetRawPayloadBytes: TBytes;
+begin
+  if (PayloadType.HasTrait(ptAsciiFormatted)) then
     Exit(TEncoding.ASCII.GetBytes(Payload));
 
-  if (PayloadTypes.IsPayloadTypeInSet(PayloadType.Base58Formatted)) then
+  if (PayloadType.HasTrait(ptBase58Formatted)) then
     Exit(TPascalBase58Encoding.Decode(Payload));
 
-  if (PayloadTypes.IsPayloadTypeInSet(PayloadType.HexFormatted)) then
+  if (PayloadType.HasTrait(ptHexFormatted)) then
     Exit(THexEncoding.Decode(Payload));
 
   raise EPascalCoinException.CreateRes(@SUnknownPayloadEncoding);
 end;
 
+function TEPasa.ToClassicPASAString : String;
+begin
+  Result := ToString(True);
+end;
+
+function TEPasa.ToString: String;
+begin
+  Result := ToString(False);
+end;
+
 function TEPasa.ToString(AOmitExtendedChecksum: Boolean): String;
 var
   LPayloadContent: String;
 begin
   Result := string.Empty;
-  if (PayloadTypes.IsPayloadTypeInSet(PayloadType.AddressedByName)) then begin
+  if PayloadType.HasTrait(ptNonDeterministic) then Exit;
+
+  if (PayloadType.HasTrait(ptAddressedByName)) then begin
     Result := Result + TPascal64Encoding.Escape(AccountName);
   end else begin
+    if (Not Account.HasValue) then Exit;
     Result := Result + Account.Value.ToString();
     if (AccountChecksum.HasValue) then begin
       Result := Result + String.Format('-%u', [AccountChecksum.Value]);
     end;
   end;
 
-  if (PayloadTypes.IsPayloadTypeInSet(PayloadType.AsciiFormatted)) then begin
+  if (PayloadType.HasTrait(ptAsciiFormatted)) then begin
     LPayloadContent := String.Format('"%s"', [TPascalAsciiEncoding.Escape(Payload)]);
-  end else if (PayloadTypes.IsPayloadTypeInSet(PayloadType.HexFormatted)) then begin
+  end else if (PayloadType.HasTrait(ptHexFormatted)) then begin
     LPayloadContent := string.Format('0x%s', [Payload]);
-  end else if (PayloadTypes.IsPayloadTypeInSet(PayloadType.Base58Formatted)) then begin
+  end else if (PayloadType.HasTrait(ptBase58Formatted)) then begin
     LPayloadContent := string.Format('%s', [Payload]);
   end else begin
     // it is non-deterministic, so payload content is ignored
     LPayloadContent := string.Empty;
   end;
 
-  if (PayloadTypes.IsPayloadTypeInSet(PayloadType.Public)) then begin
+  if (PayloadType.HasTrait(ptPublic)) then begin
     Result := Result + string.Format('[%s]', [LPayloadContent]);
-  end else if (PayloadTypes.IsPayloadTypeInSet(PayloadType.RecipientKeyEncrypted)) then begin
+  end else if (PayloadType.HasTrait(ptRecipientKeyEncrypted)) then begin
     Result := Result + string.Format('(%s)', [LPayloadContent]);
-  end else if (PayloadTypes.IsPayloadTypeInSet(PayloadType.SenderKeyEncrypted)) then begin
+  end else if (PayloadType.HasTrait(ptSenderKeyEncrypted)) then begin
     Result := Result + string.Format('<%s>', [LPayloadContent]);
-  end else if (PayloadTypes.IsPayloadTypeInSet(PayloadType.PasswordEncrypted)) then begin
+  end else if (PayloadType.HasTrait(ptPasswordEncrypted)) then begin
     Result := Result + string.Format('{%s:%s}', [LPayloadContent, TPascalAsciiEncoding.Escape(Password)]);
   end else begin
     // it is non-deterministic, so payload omitted entirely
   end;
 
   if (not AOmitExtendedChecksum) then begin
+    if (ExtendedChecksum='') then begin
+      // Need to compute:
+      ExtendedChecksum := TEPasaComp.ComputeExtendedChecksum(Result);
+    end;
     Result := Result + string.Format(':%s', [ExtendedChecksum]);
   end;
 end;
 
-function TEPasa.ToString: String;
+
+
+class function TEPasa.TryParse(const AEPasaText: String; out AEPasa: TEPasa): Boolean;
 begin
-  Result := ToString(False);
+  Result := TryParse(AEPasaText,False,AEPasa);
 end;
 
-class function TEPasa.TryParse(const AEPasaText: String; out AEPasa: TEPasa): Boolean;
+class function TEPasa.TryParse(const AEPasaText: String; AOmitExtendedChecksumVerification: Boolean; out AEPasa: TEPasa): Boolean;
 var
   LParser: TEPasaParser;
   LDisposables : TDisposables;
-
+  LEPasaErrorCode : EPasaErrorCode;
 begin
   LParser := LDisposables.AddObject( TEPasaParser.Create() ) as TEPasaParser;
-  Result := LParser.TryParse(AEPasaText, AEPasa);
+  Result := LParser.TryParse(AEPasaText,AOmitExtendedChecksumVerification,AEPasa,LEPasaErrorCode);
 end;
 
 class function TEPasa.Parse(const AEPasaText: String): TEPasa;
@@ -389,16 +496,23 @@ begin
 end;
 
 
+class function TEPasa.GetEmptyValue : TEPasa;
+begin
+  Result := EmptyEPasa;
+end;
+
 { TEPasaParser }
 
 class constructor TEPasaParser.CreateRegexEPasaParser;
 begin
   FEPasaRegex := TCustomRegex.Create(EPasaPattern);
+  FEPasaLocker := TCriticalSection.Create;
 end;
 
 class destructor TEPasaParser.DestroyRegexEPasaParser;
 begin
   FEPasaRegex.Free;
+  FEPasaLocker.Free;
 end;
 
 function TEPasaParser.Parse(const AEPasaText: String): TEPasa;
@@ -419,21 +533,28 @@ begin
 end;
 
 function TEPasaParser.TryParse(const AEPasaText: String; out AEPasa: TEPasa; out AErrorCode: EPasaErrorCode): Boolean;
+begin
+  Result := TryParse(AEPasaText,False,AEPasa,AErrorCode);
+end;
+
+function TEPasaParser.TryParse(const AEPasaText: String; AOmitExtendedChecksumVerification: Boolean; out AEPasa: TEPasa; out AErrorCode: EPasaErrorCode): Boolean;
 var
   LChecksumDelim, LAccountNumber, LAccountChecksum, LAccountName, LPayloadStartChar,
   LPayloadEndChar, LPayloadContent, LPayloadPasswordDelim, LPayloadPassword,
   LExtendedChecksumDelim, LExtendedChecksum, LActualChecksum: String;
   LAccNo, LAccChecksum: UInt32;
   LActualAccountChecksum: Byte;
-  LEPasa : TEPasa;
 begin
   AErrorCode := EPasaErrorCode.Success;
-  AEPasa := LEPasa;
+  AEPasa.Clear;
   if (string.IsNullOrEmpty(AEPasaText)) then begin
     AErrorCode := EPasaErrorCode.BadFormat;
     Exit(False);
   end;
 
+  FEPasaLocker.Acquire; // Protect against multithread
+  Try
+
   FEPasaRegex.Match(AEPasaText);
 
   LChecksumDelim := FEPasaRegex.GetMatchFromName('ChecksumDelim');
@@ -454,6 +575,10 @@ begin
     Exit(False);
   end;
 
+  Finally
+    FEPasaLocker.Release;
+  End;
+
   if (LAccountName <> #0) then begin
     // Account Name
     if (string.IsNullOrEmpty(LAccountName)) then begin
@@ -465,8 +590,7 @@ begin
     // when multiple enums are OR'ed in C#, they are combined and
     // if any of the enums numeric value is zero, it is excluded.
     // in our case,"PayloadType.NonDeterministic" is always zero so we exclude it from our set.
-    AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.AddressedByName] -
-      [PayloadType.NonDeterministic];
+    AEPasa.PayloadType := AEPasa.PayloadType + [ptAddressedByName] -[ptNonDeterministic];
     AEPasa.AccountName := TPascal64Encoding.Unescape(LAccountName);
     AEPasa.Account := Nil;
     AEPasa.AccountChecksum := Nil;
@@ -504,23 +628,23 @@ begin
           AErrorCode := EPasaErrorCode.MismatchedPayloadEncoding;
           Exit(False);
         end;
-        AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.Public] -
-          [PayloadType.NonDeterministic];
+        AEPasa.PayloadType := AEPasa.PayloadType + [ptPublic] -
+          [ptNonDeterministic];
       end;
      '(': begin
         if (LPayloadEndChar <> ')') then begin
           AErrorCode := EPasaErrorCode.MismatchedPayloadEncoding;
           Exit(False);
         end;
-        AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.RecipientKeyEncrypted] - [PayloadType.NonDeterministic];
+        AEPasa.PayloadType := AEPasa.PayloadType + [ptRecipientKeyEncrypted] - [ptNonDeterministic];
       end;
     '<': begin
         if (LPayloadEndChar <> '>') then begin
           AErrorCode := EPasaErrorCode.MismatchedPayloadEncoding;
           Exit(False);
         end;
-        AEPasa.PayloadTypes := AEPasa.PayloadTypes +
-          [PayloadType.SenderKeyEncrypted] - [PayloadType.NonDeterministic];
+        AEPasa.PayloadType := AEPasa.PayloadType +
+          [ptSenderKeyEncrypted] - [ptNonDeterministic];
       end;
 
     '{': begin
@@ -528,13 +652,13 @@ begin
           AErrorCode := EPasaErrorCode.MismatchedPayloadEncoding;
           Exit(False);
         end;
-        AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.PasswordEncrypted] - [PayloadType.NonDeterministic];
+        AEPasa.PayloadType := AEPasa.PayloadType + [ptPasswordEncrypted] - [ptNonDeterministic];
       end
       else raise ENotSupportedException.CreateResFmt(@SUnRecognizedStartCharacter, [LPayloadStartChar]);
   end;
 
   // Password
-  if (AEPasa.PayloadTypes.IsPayloadTypeInSet(PayloadType.PasswordEncrypted)) then begin
+  if (AEPasa.PayloadType.HasTrait(ptPasswordEncrypted)) then begin
     if (LPayloadPasswordDelim = #0) then begin
       AErrorCode := EPasaErrorCode.MissingPassword;
       Exit(False);
@@ -551,27 +675,27 @@ begin
     if (LPayloadContent = #0) then begin
       AEPasa.Payload := string.Empty;
     end else if (LPayloadContent.StartsWith('"')) then begin
-      AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.AsciiFormatted] - [PayloadType.NonDeterministic];
+      AEPasa.PayloadType := AEPasa.PayloadType + [ptAsciiFormatted] - [ptNonDeterministic];
       AEPasa.Payload := TPascalAsciiEncoding.UnEscape(LPayloadContent.Trim(['"']));
     end else if (LPayloadContent.StartsWith('0x')) then begin
-      AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.HexFormatted] - [PayloadType.NonDeterministic];
+      AEPasa.PayloadType := AEPasa.PayloadType + [ptHexFormatted] - [ptNonDeterministic];
       AEPasa.Payload := System.Copy(LPayloadContent, 3, LPayloadContent.Length - 2);
     end else begin
-      AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.Base58Formatted] - [PayloadType.NonDeterministic];
+      AEPasa.PayloadType := AEPasa.PayloadType + [ptBase58Formatted] - [ptNonDeterministic];
       AEPasa.Payload := LPayloadContent;
     end;
   end;
 
   // Payload Lengths
-  if (not TEPasaHelper.IsValidPayloadLength(AEPasa.PayloadTypes, AEPasa.Payload)) then begin
+  if (not TEPasaComp.IsValidPayloadLength(AEPasa.PayloadType, AEPasa.Payload)) then begin
     AErrorCode := EPasaErrorCode.PayloadTooLarge;
     Exit(False);
   end;
 
   // Extended Checksum
-  LActualChecksum := TEPasaHelper.ComputeExtendedChecksum(AEPasa.ToString(True));
+  LActualChecksum := TEPasaComp.ComputeExtendedChecksum(AEPasa.ToString(True));
   if (LExtendedChecksumDelim <> #0) then begin
-    if (LExtendedChecksum <> LActualChecksum) then begin
+    if (LExtendedChecksum <> LActualChecksum) and (Not AOmitExtendedChecksumVerification) then begin
       AErrorCode := EPasaErrorCode.BadExtendedChecksum;
       Exit(False);
     end;
@@ -582,16 +706,16 @@ begin
 end;
 
 
-{ TEPasaHelper }
+{ TEPasaComp }
 
-class function TEPasaHelper.ReadUInt16AsBytesLE(AValue: UInt16): TArray<Byte>;
+class function TEPasaComp.ReadUInt16AsBytesLE(AValue: UInt16): TArray<Byte>;
 begin
   System.SetLength(Result, System.SizeOf(UInt16));
   Result[0] := Byte(AValue);
   Result[1] := Byte(AValue shr 8);
 end;
 
-class function TEPasaHelper.ComputeExtendedChecksum(const AText: String): String;
+class function TEPasaComp.ComputeExtendedChecksum(const AText: String): String;
 var
   LHashInstance: IHashWithKey;
   LChecksum: UInt16;
@@ -602,53 +726,53 @@ begin
   Result := THexEncoding.Encode(ReadUInt16AsBytesLE(LChecksum), True);
 end;
 
-class function TEPasaHelper.IsValidExtendedChecksum(const AText, AChecksum: String): Boolean;
+class function TEPasaComp.IsValidExtendedChecksum(const AText, AChecksum: String): Boolean;
 begin
   Result := ComputeExtendedChecksum(AText) = AChecksum;
 end;
 
-class function TEPasaHelper.IsValidPayloadLength(APayloadType: PayloadTypes; const APayloadContent: String): Boolean;
+class function TEPasaComp.IsValidPayloadLength(APayloadType: TPayloadType; const APayloadContent: String): Boolean;
 begin
   if (string.IsNullOrEmpty(APayloadContent)) then
     Exit(True);
 
-  if (APayloadType.IsPayloadTypeInSet(PayloadType.Public)) then begin
+  if (APayloadType.HasTrait(ptPublic)) then begin
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.AsciiFormatted)) then
+    if (APayloadType.HasTrait(ptAsciiFormatted)) then
       Exit(TPascalAsciiEncoding.UnEscape(APayloadContent).Length <= MaxPublicAsciiContentLength);
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.HexFormatted)) then
+    if (APayloadType.HasTrait(ptHexFormatted)) then
       Exit(APayloadContent.Length <= MaxPublicHexContentLength);
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.Base58Formatted)) then
+    if (APayloadType.HasTrait(ptBase58Formatted)) then
       Exit(APayloadContent.Length <= MaxPublicBase58ContentLength);
 
     // unknown encoding format
     Result := False;
   end;
 
-  if (APayloadType.IsPayloadTypeInSet(PayloadType.SenderKeyEncrypted) or APayloadType.IsPayloadTypeInSet(PayloadType.RecipientKeyEncrypted)) then begin
+  if (APayloadType.HasTrait(ptSenderKeyEncrypted) or APayloadType.HasTrait(ptRecipientKeyEncrypted)) then begin
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.AsciiFormatted)) then
+    if (APayloadType.HasTrait(ptAsciiFormatted)) then
       Exit(TPascalAsciiEncoding.UnEscape(APayloadContent).Length <= MaxECIESAsciiContentLength);
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.HexFormatted)) then
+    if (APayloadType.HasTrait(ptHexFormatted)) then
       Exit(APayloadContent.Length <= MaxECIESHexContentLength);
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.Base58Formatted)) then
+    if (APayloadType.HasTrait(ptBase58Formatted)) then
       Exit(APayloadContent.Length <= MaxECIESBase58ContentLength);
     // unknown encoding format
     Result := False;
   end;
 
-  if (APayloadType.IsPayloadTypeInSet(PayloadType.PasswordEncrypted)) then begin
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.AsciiFormatted)) then
+  if (APayloadType.HasTrait(ptPasswordEncrypted)) then begin
+    if (APayloadType.HasTrait(ptAsciiFormatted)) then
       Exit(TPascalAsciiEncoding.UnEscape(APayloadContent).Length <= MaxAESAsciiContentLength);
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.HexFormatted)) then
+    if (APayloadType.HasTrait(ptHexFormatted)) then
       Exit(APayloadContent.Length <= MaxAESHexContentLength);
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.Base58Formatted)) then
+    if (APayloadType.HasTrait(ptBase58Formatted)) then
       Exit(APayloadContent.Length <= MaxAESBase58ContentLength);
 
     // unknown encoding format
@@ -659,10 +783,61 @@ begin
   Result := False;
 end;
 
-class function TEPasaHelper.IsValidPasswordLength(const APasswordValue : String): Boolean;
+class function TEPasaComp.IsValidPasswordLength(const APasswordValue : String): Boolean;
 begin
   // no password length policy established (only client-side concern)
   Result := True;
 end;
 
+class function TEPasaComp.GetPayloadTypeProtocolByte(const APayloadType : TPayloadType) : Byte;
+var
+ LPayloadType : TPayloadTrait;
+begin
+  Result := 0; // NonDeterministic by default
+  for LPayloadType := Low(TPayloadTrait) to High(TPayloadTrait) do
+    if APayloadType.HasTrait(LPayloadType) then
+      Result := Result OR LPayloadType.ToProtocolValue;
+end;
+
+class function TEPasaComp.GetPayloadTypeFromProtocolByte(AByte: Byte) : TPayloadType;
+var
+ LPayloadType : TPayloadTrait;
+ LPayloadTypeByte : byte;
+begin
+  if AByte = 0 then
+    Exit([ptNonDeterministic]);
+
+  Result := [];
+  for LPayloadType := Low(TPayloadTrait) to High(TPayloadTrait) do begin
+    LPayloadTypeByte := LPayloadType.ToProtocolValue;
+    if (AByte AND LPayloadTypeByte) = LPayloadTypeByte then
+      Result := Result + [LPayloadType];
+  end;
+end;
+
+class function TEPasaComp.FromProtocolValue(AVal : Byte) : TPayloadType;
+begin
+  if AVal = 0 then begin
+    Exit([ptNonDeterministic]);
+  end;
+  Result := [];
+  if AVal AND BYTE_BIT_0 <> 0 then Result := Result + [ptPublic];
+  if AVal AND BYTE_BIT_1 <> 0 then Result := Result + [ptRecipientKeyEncrypted];
+  if AVal AND BYTE_BIT_2 <> 0 then Result := Result + [ptSenderKeyEncrypted];
+  if AVal AND BYTE_BIT_3 <> 0 then Result := Result + [ptPasswordEncrypted];
+  if AVal AND BYTE_BIT_4 <> 0 then Result := Result + [ptAsciiFormatted];
+  if AVal AND BYTE_BIT_5 <> 0 then Result := Result + [ptHexFormatted];
+  if AVal AND BYTE_BIT_6 <> 0 then Result := Result + [ptBase58Formatted];
+  if AVal AND BYTE_BIT_7 <> 0 then Result := Result + [ptAddressedByName];
+end;
+
+
+
+
+initialization
+{$IFDEF FPC}
+FillChar(EmptyEPasa, SizeOf(EmptyEPASA), 0);
+{$ELSE}
+EmptyEPasa := Default(TEPasa);
+{$ENDIF}
 end.

+ 298 - 0
src/core/UEPasaDecoder.pas

@@ -0,0 +1,298 @@
+unit UEPasaDecoder;
+
+{ Copyright (c) PascalCoin Developers - Herman Schoenfeld - Albert Molina
+
+  PIP-0027: E-PASA Reference Implementation
+  See: https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0027.md
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{$IFDEF FPC}
+  {$MODE DELPHI}
+  {$ZEROBASEDSTRINGS OFF}
+{$ENDIF FPC}
+
+interface
+
+{$I ./../config.inc}
+
+uses
+  SysUtils,
+  TypInfo,
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
+  UBlockChain, UNode, UBaseTypes, UPCDataTypes,
+  UAccounts,
+  UEncoding,
+  UEPasa,
+  UWallet,
+  URPC, UJSONFunctions;
+
+type
+  TDecodeEPasaResult = (der_Decoded, der_Undefined, der_NonDeterministic, der_InvalidPayloadType, der_AccountNameNotFound, der_NotEnoughData, der_PrivateKeyNotFound, der_PasswordNotFound);
+
+  TEPasaDecoder = Class
+  public
+    class Function TryDecodeEPASA(AAccount : Cardinal; const APayload : TOperationPayload; const ANode : TNode; const AWalletKeys : TWalletKeys; const APasswords : TList<String>;
+      out ADecodeEPasaResult : TDecodeEPasaResult; out AEPasa : TEPasa) : Boolean; overload;
+    class Function TryDecodeEPASA(AAccount : Cardinal; const APayload : TOperationPayload; const ANode : TNode; const AWalletKeys : TWalletKeys; const APasswords : TList<String>;
+      out AEPasa : TEPasa) : Boolean; overload;
+    class Function DecodeEPASA(AAccount : Cardinal; const APayload : TOperationPayload; const ANode : TNode; const AWalletKeys : TWalletKeys; const APasswords : TList<String>) : String; overload;
+    class function CheckEPasa(const ASender : TRPCProcess; const AAccount_EPasa : String; AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean; overload;
+    class function CheckEPasa(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean; overload;
+    class function ValidateEPasa(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+  End;
+
+implementation
+
+uses UPCEncryption, UCommon, UCrypto;
+
+{ TEPasaDecoder }
+
+class function TEPasaDecoder.TryDecodeEPASA(AAccount: Cardinal;
+  const APayload: TOperationPayload; const ANode : TNode; const AWalletKeys: TWalletKeys;
+  const APasswords: TList<String>; out ADecodeEPasaResult: TDecodeEPasaResult;
+  out AEPasa: TEPasa): Boolean;
+var
+  LUnencryptedPayloadBytes, LPwd : TBytes;
+  LDone : Boolean;
+  i : Integer;
+  LAccount : TAccount;
+begin
+  LUnencryptedPayloadBytes := Nil;
+  AEPasa.Clear;
+  Result := False;
+  ADecodeEPasaResult := der_Decoded;
+  AEPasa.Account := AAccount;
+  AEPasa.AccountChecksum := TEPasa.CalculateAccountChecksum(AAccount);
+  AEPasa.PayloadType := TEPasaComp.FromProtocolValue(APayload.payload_type);
+  if AEPasa.PayloadType.HasTrait(ptNonDeterministic) then begin
+    ADecodeEPasaResult := der_NonDeterministic;
+    Exit(False);
+  end;
+  if Not AEPasa.PayloadType.IsValid then begin
+    ADecodeEPasaResult := der_InvalidPayloadType;
+    Exit(False);
+  end;
+
+  if AEPasa.PayloadType.HasTrait(ptAddressedByName) then begin
+    if (AEPasa.PayloadType.HasTrait(ptPublic) and
+       AEPasa.PayloadType.HasTrait(ptBase58Formatted)) then begin
+      // PayToKey candidate...
+      AEPasa.AccountName := '@';
+    end else begin
+      if Assigned(ANode) then begin
+        LAccount := ANode.GetMempoolAccount(AAccount);
+        AEPasa.AccountName := LAccount.name.ToPrintable;
+      end;
+      if AEPasa.AccountName='' then begin
+        ADecodeEPasaResult := der_AccountNameNotFound; // Will continue processing
+      end;
+    end;
+  end;
+
+  // payload data
+  if (Length(APayload.payload_raw)=0) then begin
+    // Nothing to decode...
+  end else if (AEPasa.PayloadType.HasTrait(ptSenderKeyEncrypted)) or (AEPasa.PayloadType.HasTrait(ptRecipientKeyEncrypted)) then begin
+    if Assigned(AWalletKeys) then begin
+      LDone := False;
+      i := 0;
+      while (Not LDone) and (i < AWalletKeys.Count) do begin
+        if Assigned(AWalletKeys.Key[i].PrivateKey) then begin
+          if TPCEncryption.DoPascalCoinECIESDecrypt(AWalletKeys.Key[i].PrivateKey.PrivateKey,APayload.payload_raw,LUnencryptedPayloadBytes) then begin
+            LDone := True;
+          end;
+        end;
+        inc(i);
+      end;
+      if Not LDone then begin
+        ADecodeEPasaResult := der_PrivateKeyNotFound;
+        Exit(False);
+      end;
+    end else begin
+      ADecodeEPasaResult := der_NotEnoughData;
+      Exit(False);
+    end;
+  end else if (AEPasa.PayloadType.HasTrait(ptPasswordEncrypted)) then begin
+    if Assigned(APasswords) then begin
+      LDone := False;
+      i := 0;
+      while (Not LDone) and (i < APasswords.Count) do begin
+        LPwd.FromString(APasswords[i]);
+        if TPCEncryption.DoPascalCoinAESDecrypt(APayload.payload_raw,LPwd,LUnencryptedPayloadBytes) then begin
+          AEPasa.Password := APasswords[i];
+          LDone := True;
+        end;
+        inc(i);
+      end;
+      if Not LDone then begin
+        ADecodeEPasaResult := der_PasswordNotFound;
+        Exit(False);
+      end;
+    end else begin
+      ADecodeEPasaResult := der_NotEnoughData;
+      Exit(False);
+    end;
+  end else begin
+    if (Not AEPasa.PayloadType.HasTrait(ptPublic)) then begin
+      // Internal Error
+      ADecodeEPasaResult := der_Undefined;
+      Exit(False);
+    end;
+    LUnencryptedPayloadBytes := APayload.payload_raw;
+  end;
+  // LUnencryptedPayloadBytes Has Value in RAW
+  if (AEPasa.PayloadType.HasTrait(ptAsciiFormatted)) then begin
+    AEPasa.Payload := LUnencryptedPayloadBytes.ToString;
+  end else if (AEPasa.PayloadType.HasTrait(ptHexFormatted)) then begin
+    AEPasa.Payload := THexEncoding.Encode(LUnencryptedPayloadBytes,True);
+  end else if (AEPasa.PayloadType.HasTrait(ptBase58Formatted)) then begin
+    AEPasa.Payload := TPascalBase58Encoding.Encode(LUnencryptedPayloadBytes);
+  end else begin
+    // Internal error
+    ADecodeEPasaResult := der_Undefined;
+    Exit(False);
+  end;
+  Result := true;
+end;
+
+class function TEPasaDecoder.CheckEPasa(const ASender: TRPCProcess;
+  const AMethodName: String; AInputParams, AJSONResponse: TPCJSONObject;
+  var AErrorNum: Integer; var AErrorDesc: String): Boolean;
+begin
+  Result := CheckEPAsa(ASender,AInputParams.AsString('account_epasa',''),AJSONResponse,AErrorNum,AErrorDesc);
+end;
+
+class function TEPasaDecoder.CheckEPasa(const ASender: TRPCProcess;
+  const AAccount_EPasa: String; AJSONResponse: TPCJSONObject;
+  var AErrorNum: Integer; var AErrorDesc: String): Boolean;
+var LEPasa : TEPasa;
+begin
+  if Not TEPasa.TryParse(AAccount_EPasa,LEPasa) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidEPASA;
+    AErrorDesc := 'Not a valid epasa: '+AAccount_EPasa;
+    Result := False;
+    Exit(False);
+  end else begin
+    Result := True;
+    AJSONResponse.GetAsVariant('account_epasa').Value := LEPasa.ToString(False);
+    AJSONResponse.GetAsVariant('account_epasa_classic').Value := LEPasa.ToClassicPASAString;
+
+    if LEPasa.PayloadType.HasTrait(ptAddressedByName) then begin
+      AJSONResponse.GetAsVariant('account').Value := LEPasa.AccountName;
+    end else begin
+      AJSONResponse.GetAsVariant('account').Value := LEPasa.Account.Value;
+    end;
+
+    if LEPasa.PayloadType.HasTrait(ptPublic) then begin
+      AJSONResponse.GetAsVariant('payload_method').Value := 'none';
+    end else if LEPasa.PayloadType.HasTrait(ptSenderKeyEncrypted) then begin
+      AJSONResponse.GetAsVariant('payload_method').Value := 'sender';
+    end else if LEPasa.PayloadType.HasTrait(ptRecipientKeyEncrypted) then begin
+      AJSONResponse.GetAsVariant('payload_method').Value := 'dest';
+    end else if LEPasa.PayloadType.HasTrait(ptPasswordEncrypted) then begin
+      AJSONResponse.GetAsVariant('payload_method').Value := 'aes';
+      AJSONResponse.GetAsVariant('pwd').Value := LEPasa.Password;
+    end;
+
+    if LEPasa.PayloadType.HasTrait(ptAsciiFormatted) then begin
+      AJSONResponse.GetAsVariant('payload_encode').Value := 'string';
+    end else if LEPasa.PayloadType.HasTrait(ptHexFormatted) then begin
+      AJSONResponse.GetAsVariant('payload_encode').Value := 'hexa';
+    end else if LEPasa.PayloadType.HasTrait(ptBase58Formatted) then begin
+      AJSONResponse.GetAsVariant('payload_encode').Value := 'base58';
+    end;
+
+    AJSONResponse.GetAsVariant('payload').Value := LEPasa.GetRawPayloadBytes.ToHexaString;
+    AJSONResponse.GetAsVariant('payload_type').Value := LEPasa.PayloadType.ToProtocolValue;
+    AJSONResponse.GetAsVariant('is_pay_to_key').Value := LEPasa.IsPayToKey;
+  end;
+end;
+
+class function TEPasaDecoder.DecodeEPASA(AAccount: Cardinal;
+  const APayload: TOperationPayload; const ANode: TNode;
+  const AWalletKeys: TWalletKeys; const APasswords: TList<String>): String;
+var LEPasa : TEPasa;
+begin
+  if TryDecodeEPASA(AAccount,APayload,ANode,AWalletKeys,APasswords,LEPasa) then begin
+    Result := LEPasa.ToClassicPASAString;
+  end else Result := '';
+end;
+
+class function TEPasaDecoder.TryDecodeEPASA(AAccount: Cardinal;
+  const APayload: TOperationPayload; const ANode : TNode; const AWalletKeys: TWalletKeys;
+  const APasswords: TList<String>; out AEPasa: TEPasa): Boolean;
+var LDecodeEPasaResult: TDecodeEPasaResult;
+begin
+  Result := TryDecodeEPASA(AAccount,APayload,ANode,AWalletKeys,APasswords,LDecodeEPasaResult,AEPasa);
+end;
+
+class function TEPasaDecoder.ValidateEPasa(const ASender: TRPCProcess;
+  const AMethodName: String; AInputParams, AJSONResponse: TPCJSONObject;
+  var AErrorNum: Integer; var AErrorDesc: String): Boolean;
+var
+  s : String;
+  card : Cardinal;
+  LEPasaStr, LDelimStart,LDelimEnd, LPwdZone, LPayload : String;
+  LRawPayload : TRawBytes;
+begin
+  LEPasaStr := '';
+  LPwdZone := '';
+  LEPasaStr := AInputParams.AsString('account','');
+  s := Trim(AInputParams.AsString('payload_method','none'));
+  if s='dest' then begin
+    LDelimStart := '(';
+    LDelimEnd := ')';
+  end else if s='sender' then begin
+    LDelimStart := '<';
+    LDelimEnd := '>';
+  end else if s='aes' then begin
+    LDelimStart := '{';
+    LDelimEnd := '}';
+    LPwdZone := ':' + AInputParams.AsString('pwd','');
+  end else if (s='none') or (trim(s)='') then begin
+    LDelimStart := '[';
+    LDelimEnd := ']';
+  end else begin
+    AErrorNum := CT_RPC_ErrNum_InvalidData;
+    AErrorDesc := Format('"payload_method" %s not valid',[s]);
+    Exit(False);
+  end;
+  s := Trim(AInputParams.AsString('payload',''));
+  if Not TCrypto.HexaToRaw(s,LRawPayload) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidData;
+    AErrorDesc := Format('"payload" is not an HEXASTRING: %s',[s]);
+    Exit(False);
+  end;
+  s := Trim(AInputParams.AsString('payload_encode','string'));
+  if s='hexa' then begin
+    LPayload := '0x'+LRawPayload.ToHexaString;
+  end else if s='base58' then begin
+    LPayload := TPascalBase58Encoding.Encode(LRawPayload);
+  end else if (s='string') or (Trim(s)='') then begin
+    LPayload := '"'+TPascalAsciiEncoding.Escape(LRawPayload.ToString)+'"';
+  end else begin
+    AErrorNum := CT_RPC_ErrNum_InvalidData;
+    AErrorDesc := Format('"payload_encode" %s not valid',[s]);
+    Exit(False);
+  end;
+
+  LEPasaStr := AInputParams.AsString('account','') + LDelimStart + LPayload + LPwdZone + LDelimEnd;
+  Result := CheckEPasa(ASender,LEPasaStr,AJSONResponse,AErrorNum,AErrorDesc);
+end;
+
+initialization
+  TRPCProcess.RegisterProcessMethod('validateepasa',TEPasaDecoder.ValidateEPasa);
+  TRPCProcess.RegisterProcessMethod('checkepasa',TEPasaDecoder.CheckEPasa);
+finalization
+  TRPCProcess.UnregisterProcessMethod('validateepasa');
+  TRPCProcess.UnregisterProcessMethod('checkepasa');
+end.

+ 1 - 1
src/core/UEncoding.pas

@@ -106,7 +106,7 @@ type
     public
       const
         CharSet = '0123456789abcdef';
-        NibblePattern = '[0-9a-f]';
+        NibblePattern = '[0-9a-fA-F]';
         BytePattern = NibblePattern + '{2}';
         SubStringPattern = '(?:' + BytePattern + ')+';
         StringPattern = SubStringPattern + '$';

+ 16 - 11
src/core/UNetProtocol.pas

@@ -420,7 +420,6 @@ Type
     Procedure DoProcess_GetPendingOperations;
     Procedure SetClient(Const Value : TNetTcpIpClient);
     Function ReadTcpClientBuffer(MaxWaitMiliseconds : Cardinal; var HeaderData : TNetHeaderData; BufferData : TStream) : Boolean;
-    Procedure DisconnectInvalidClient(ItsMyself : Boolean; Const why : String);
     function GetClient: TNetTcpIpClient;
   protected
     Procedure Send(NetTranferType : TNetTransferType; operation, errorcode : Word; request_id : Integer; DataBuffer : TStream);
@@ -434,6 +433,7 @@ Type
     Function ConnectTo(ServerIP: String; ServerPort:Word) : Boolean;
     Property Connected : Boolean read GetConnected write SetConnected;
     Property IsConnecting : Boolean read FIsConnecting;
+    Procedure DisconnectInvalidClient(ItsMyself : Boolean; Const why : String);
     Function Send_Hello(NetTranferType : TNetTransferType; request_id : Integer) : Boolean;
     Function Send_NewBlockFound(Const NewBlock : TPCOperationsComp) : Boolean;
     Function Send_GetBlocks(StartAddress, quantity : Cardinal; var request_id : Cardinal) : Boolean;
@@ -3397,7 +3397,7 @@ end;
 procedure TNetConnection.DoProcess_GetPubkeyAccounts_Request(HeaderData: TNetHeaderData; DataBuffer: TStream);
 Const CT_Max_Accounts_per_call = 1000;
 var responseStream, accountsStream : TMemoryStream;
-  start,max : Integer;
+  start,max,i : Integer;
   c, nAccounts : Cardinal;
   acc : TAccount;
   DoDisconnect : Boolean;
@@ -3405,6 +3405,7 @@ var responseStream, accountsStream : TMemoryStream;
   pubKey : TAccountKey;
   sbakl : TSafeboxPubKeysAndAccounts;
   ocl : TAccountsNumbersList;
+  LAccountsList : TList<Integer>;
 begin
   {
   This call is used to obtain Accounts used by a Public key
@@ -3451,15 +3452,19 @@ begin
     if Assigned(sbakl) then begin
       ocl := sbakl.GetAccountsUsingThisKey(pubKey);
       if Assigned(ocl) then begin
-        while (start<ocl.Count) And (max>0) do begin
-          acc := TNode.Node.GetMempoolAccount(ocl.Get(start));
-          if (HeaderData.protocol.protocol_available>9) then
-            TAccountComp.SaveAccountToAStream(accountsStream,acc,CT_PROTOCOL_5)
-          else
-            TAccountComp.SaveAccountToAStream(accountsStream,acc,CT_PROTOCOL_4);
-          inc(nAccounts);
-          inc(start);
-          dec(max);
+        LAccountsList := TList<Integer>.Create;
+        try
+          ocl.FillList(start,max,LAccountsList);
+          for i := 0 to LaccountsList.Count-1 do begin
+            acc := TNode.Node.GetMempoolAccount(LAccountsList[i]);
+            if (HeaderData.protocol.protocol_available>9) then
+              TAccountComp.SaveAccountToAStream(accountsStream,acc,CT_PROTOCOL_5)
+            else
+              TAccountComp.SaveAccountToAStream(accountsStream,acc,CT_PROTOCOL_4);
+          end;
+          nAccounts := LaccountsList.Count;
+        finally
+          LaccountsList.Free;
         end;
       end;
       // Save & send

+ 239 - 3
src/core/UNode.pas

@@ -35,8 +35,8 @@ interface
 
 uses
   Classes, SysUtils,
-  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF}, UPCDataTypes,
-  UBlockChain, UNetProtocol, UAccounts, UCrypto, UThread, SyncObjs, ULog, UBaseTypes, UPCOrderedLists;
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF}, UPCDataTypes, UEncoding,
+  UBlockChain, UNetProtocol, UAccounts, UCrypto, UEPasa, UThread, SyncObjs, ULog, UBaseTypes, UPCOrderedLists;
 
 {$I ./../config.inc}
 
@@ -64,6 +64,7 @@ Type
     FSentOperations : TOrderedRawList;
     FBroadcastData : Boolean;
     FUpdateBlockchain: Boolean;
+    FMaxPayToKeyPurchasePrice: Int64;
     {$IFDEF BufferOfFutureOperations}
     FBufferAuxWaitingOperations : TOperationsHashTree;
     {$ENDIF}
@@ -119,12 +120,24 @@ Type
     function TryLockNode(MaxWaitMilliseconds : Cardinal) : Boolean;
     procedure UnlockNode;
     //
+    function GetAccountsAvailableByPublicKey(const APubKeys : TList<TAccountKey>; out AOnSafebox, AOnMempool : Integer) : Integer; overload;
+    function GetAccountsAvailableByPublicKey(const APubKey : TAccountKey; out AOnSafebox, AOnMempool : Integer) : Integer; overload;
+    //
     Property BroadcastData : Boolean read FBroadcastData write FBroadcastData;
     Property UpdateBlockchain : Boolean read FUpdateBlockchain write FUpdateBlockchain;
     procedure MarkVerifiedECDSASignaturesFromMemPool(newOperationsToValidate : TPCOperationsComp);
     class function NodeVersion : String;
     class function GetPascalCoinDataFolder : String;
     class procedure SetPascalCoinDataFolder(const ANewDataFolder : String);
+    //
+    function TryFindAccountByKey(const APubKey : TAccountKey; out AAccountNumber : Cardinal) : Boolean;
+    function TryFindPublicSaleAccount(AMaximumPrice : Int64; APreventRaceCondition : Boolean; out AAccountNumber : Cardinal) : Boolean;
+    Function TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal): Boolean; overload;
+    Function TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AErrorMessage: String): Boolean; overload;
+    Function TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : boolean): Boolean; overload;
+    Function TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : boolean; out AErrorMessage: String): Boolean; overload;
+
+    Property MaxPayToKeyPurchasePrice: Int64 read FMaxPayToKeyPurchasePrice write FMaxPayToKeyPurchasePrice;
   End;
 
   TThreadSafeNodeNotifyEvent = Class(TPCThread)
@@ -205,7 +218,8 @@ Type
 
 implementation
 
-Uses UOpTransaction, UConst, UTime, UCommon, UPCOperationsSignatureValidator, UFolderHelper;
+Uses UOpTransaction, UConst, UTime, UCommon, UPCOperationsSignatureValidator,
+  UFolderHelper, USettings;
 
 var _Node : TNode;
   _PascalCoinDataFolder : String;
@@ -630,6 +644,7 @@ end;
 
 constructor TNode.Create(AOwner: TComponent);
 begin
+  FMaxPayToKeyPurchasePrice := 0;
   FSentOperations := TOrderedRawList.Create;
   FNodeLog := TLog.Create(Self);
   FNodeLog.ProcessGlobalLogs := false;
@@ -762,6 +777,170 @@ begin
   dec(FDisabledsNewBlocksCount);
 end;
 
+function TNode.TryFindAccountByKey(const APubKey: TAccountKey;
+  out AAccountNumber: Cardinal): Boolean;
+  // Finds the smallest numbered account with selected key (or returns false)
+var Lpka : TSafeboxPubKeysAndAccounts;
+  LAccountsNumberList : TAccountsNumbersList;
+begin
+  Result := False;
+  Lpka := Bank.SafeBox.OrderedAccountKeysList;
+  if Assigned(Lpka) then begin
+    LAccountsNumberList := Lpka.GetAccountsUsingThisKey(APubKey);
+    if Assigned(LAccountsNumberList) then begin
+      if LAccountsNumberList.Count>0 then begin
+        AAccountNumber := LAccountsNumberList.Get(0);
+        Result := True;
+      end;
+    end;
+  end;
+end;
+
+function TNode.TryFindPublicSaleAccount(AMaximumPrice: Int64; APreventRaceCondition : Boolean;
+  out AAccountNumber: Cardinal): Boolean;
+  // Finds an account at or below argument purchase price (or returns false)
+  // APreventRaceCondition: When True will return a random account in valid range price
+  // Limitations: Account must be >0
+var LtempAccNumber : Integer;
+  LLastValidAccount, LCurrAccount : TAccount;
+  LContinueSearching : Boolean;
+begin
+  Result := False;
+
+  // Sorted list: Bank.SafeBox.AccountsOrderedBySalePrice
+  // Note: List is sorted by Sale price (ASCENDING), but NOT by public/private sale, must check
+
+  if Not Bank.SafeBox.AccountsOrderedBySalePrice.FindLowest(LtempAccNumber) then Exit(False);
+  LCurrAccount := GetMempoolAccount(LtempAccNumber);
+
+  if (LCurrAccount.accountInfo.price<=AMaximumPrice)
+    and (TAccountComp.IsAccountForPublicSale(LCurrAccount.accountInfo)) then begin
+    LLastValidAccount := LCurrAccount;
+    LContinueSearching := (APreventRaceCondition) And (Random(50)=0);
+  end else begin
+    LLastValidAccount := CT_Account_NUL;
+    LContinueSearching := True;
+  end;
+
+  while (LCurrAccount.accountInfo.price<=AMaximumPrice) and (LContinueSearching) do begin
+
+    if TAccountComp.IsAccountForPublicSale(LCurrAccount.accountInfo) then LLastValidAccount := LCurrAccount;
+
+    if Not (Bank.SafeBox.AccountsOrderedBySalePrice.FindSuccessor(LtempAccNumber,LtempAccNumber)) then Break;
+    LCurrAccount := GetMempoolAccount(LtempAccNumber);
+
+    // If price increased, then do not continue and use LastValidAccount
+    if (LLastValidAccount.account>0)
+      and (LLastValidAccount.accountInfo.price <> LCurrAccount.accountInfo.price) then Break;
+
+    // Continue?
+    LContinueSearching :=
+      (LLastValidAccount.account=0) // This means that no valid account has been found yet...
+      or
+      (LContinueSearching And (Random(50)=0)); // Random prevention
+  end;
+  if (LLastValidAccount.account>0) then begin
+    AAccountNumber := LLastValidAccount.account;
+    Result := True;
+  end else begin
+    AAccountNumber := 0;
+    Result := False;
+  end;
+end;
+
+Function TNode.TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal): Boolean;
+var LErrMsg : String;
+begin
+  Result := TryResolveEPASA(AEPasa, AResolvedAccount, LErrMsg);
+end;
+
+Function TNode.TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AErrorMessage: String): Boolean;
+var
+  LAccountKey : TAccountKey;
+  LRequiresPurchase : Boolean;
+begin
+  Result := TryResolveEPASA(AEPasa, AResolvedAccount, LAccountKey, LRequiresPurchase, AErrorMessage);
+  if Result AND AEPasa.IsPayToKey then begin
+    Result := False;
+    AErrorMessage := 'EPASA was a pay-to-key style';
+  end;
+end;
+
+Function TNode.TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : boolean): Boolean;
+var LErrMsg : String;
+begin
+  Result := TryResolveEPASA(AEPasa, AResolvedAccount, AResolvedKey, ARequiresPurchase, LErrMsg);
+end;
+
+Function TNode.TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : boolean; out AErrorMessage: String): Boolean;
+var
+  LErrMsg : String;
+begin
+  AResolvedAccount := 0;
+  AResolvedKey.Clear;
+  ARequiresPurchase := False;
+  AErrorMessage := '';
+  if (AEPasa.IsPayToKey) then begin
+    // Parse account key in EPASA
+    if NOT TAccountComp.AccountPublicKeyImport(AEPasa.Payload, AResolvedKey, LErrMsg) then begin
+      AResolvedAccount := CT_AccountNo_NUL;
+      AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
+      ARequiresPurchase := False;
+      AErrorMessage := Format('Invalid key specified in PayToKey EPASA "%s". %s',[AEPasa.ToString(), LErrMsg]);
+      Exit(False);
+    end;
+
+    // Try to find key in safebox
+    if TryFindAccountByKey(AResolvedKey, AResolvedAccount) then begin
+      // Key already exists in SafeBox, so send to that account
+      ARequiresPurchase := False;
+      Exit(True);
+    end;
+
+    // If no key found, find optimal public purchase account
+    if TryFindPublicSaleAccount(MaxPayToKeyPurchasePrice, True, AResolvedAccount) then begin
+      // Account needs to be purchased
+      ARequiresPurchase := True;
+      Exit(True);
+    end;
+
+    // Account could not be resolved
+    AResolvedAccount := CT_AccountNo_NUL;
+    AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
+    ARequiresPurchase := False;
+    AErrorMessage := 'No account could be resolved for pay to key EPASA';
+    Exit(False);
+
+  end else if (AEPasa.IsAddressedByName) then begin
+    // Find account by name
+    AResolvedAccount := Bank.SafeBox.FindAccountByName(AEPasa.AccountName);
+    AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
+    ARequiresPurchase := False;
+    if AResolvedAccount = CT_AccountNo_NUL then begin
+      // No account with name found
+      AResolvedAccount := CT_AccountNo_NUL;
+      AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
+      ARequiresPurchase := False;
+      AErrorMessage := Format('No account with name "%s" was found', [AEPasa.AccountName]);
+      Exit(False);
+    end;
+    Exit(True);
+  end;
+  // addressed by number
+  if NOT AEPasa.IsAddressedByNumber then raise Exception.Create('Internal Error c8ecd69d-3621-4f5e-b4f1-9926ab2f5013');
+  if NOT AEPasa.Account.HasValue then raise Exception.Create('Internal Error 544c8cb9-b700-4b5f-93ca-4d045d0a06ae');
+  AResolvedAccount := AEPasa.Account.Value;
+  if (AResolvedAccount < 0) or (AResolvedAccount >= Self.Bank.AccountsCount) then begin
+    AResolvedAccount := CT_AccountNo_NUL;
+    AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
+    ARequiresPurchase := False;
+    AErrorMessage := Format('Account number %d does not exist in safebox',[AEPasa.Account.Value]);
+    Exit(False);
+  end;
+  Result := true;
+end;
+
+
 function TNode.TryLockNode(MaxWaitMilliseconds: Cardinal): Boolean;
 begin
   Result := TPCThread.TryProtectEnterCriticalSection(Self,MaxWaitMilliseconds,FLockMempool);
@@ -1285,6 +1464,63 @@ begin
   end;
 end;
 
+function TNode.GetAccountsAvailableByPublicKey(const APubKey: TAccountKey;
+  out AOnSafebox, AOnMempool: Integer): Integer;
+var LPubKeys: TList<TAccountKey>;
+begin
+  LPubKeys := TList<TAccountKey>.Create;
+  Try
+    LPubKeys.Add(APubKey);
+    Result := GetAccountsAvailableByPublicKey(LPubKeys,AOnSafebox,AOnMempool);
+  Finally
+    LPubKeys.Free;
+  End;
+end;
+
+function TNode.GetAccountsAvailableByPublicKey(
+  const APubKeys: TList<TAccountKey>; out AOnSafebox,
+  AOnMempool: Integer): Integer;
+var Lmempool : TPCOperationsComp;
+  i,j,k : Integer;
+  Lop : TPCOperation;
+  LopResume : TOperationResume;
+  Lpubkeys : TSafeboxPubKeysAndAccounts;
+  Laccounts : TAccountsNumbersList;
+begin
+  AOnMempool := 0;
+  AOnSafebox := 0;
+  // Check safebox
+  Lpubkeys := Bank.SafeBox.OrderedAccountKeysList;
+  if Assigned(Lpubkeys) then begin
+    for i := 0 to APubKeys.Count-1 do begin
+      Laccounts := Lpubkeys.GetAccountsUsingThisKey(APubKeys[i]);
+      if Assigned(Laccounts) then begin
+        Inc(AOnSafebox,Laccounts.Count);
+      end;
+    end;
+  end else AOnSafebox := -1;
+  for i := 0 to APubKeys.Count-1 do begin
+    // Check mempool
+    Lmempool := LockMempoolRead;
+    try
+      for j := 0 to Lmempool.Count-1 do begin
+        Lop := Lmempool.Operation[j];
+        Lop.OperationToOperationResume(Bank.BlocksCount,Lop,True,Lop.SignerAccount,LopResume);
+        for k:=0 to Length(LopResume.Changers)-1 do begin
+          if (public_key in LopResume.Changers[k].Changes_type) and (LopResume.Changers[k].New_Accountkey.IsEqualTo(APubKeys[i])) then begin
+            // New account is on the mempool!
+            inc(AOnMempool);
+          end;
+        end;
+      end;
+    finally
+      UnlockMempoolRead;
+    end;
+  end;
+  if AOnSafebox>=0 then Result := (AOnMempool + AOnsafebox)
+  else Result := AOnMempool;
+end;
+
 function TNode.GetMempoolAccount(AAccountNumber : Cardinal): TAccount;
 var LLockedMempool : TPCOperationsComp;
 begin

+ 1 - 1
src/core/UOpTransaction.pas

@@ -27,7 +27,7 @@ interface
 
 Uses UCrypto, UBlockChain, Classes, UAccounts, UBaseTypes,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
-  UPCDataTypes;
+  UPCDataTypes, UEPasa;
 
 Type
   // Operations Type

+ 182 - 229
src/core/UPCAbstractMem.pas

@@ -6,12 +6,16 @@ interface
   {$MODE DELPHI}
 {$ENDIF}
 
+{$I ./../config.inc}
+
 uses Classes, SysUtils, SyncObjs,
   UAbstractMem, UFileMem, UAbstractMemTList, UCacheMem,
-  UAbstractBTree, UThread,
+  UAbstractBTree, UThread, UAbstractMemBTree,
   UAVLCache, ULog, UCrypto,
   UPCAbstractMemAccountKeys,
   UPCDataTypes, UBaseTypes, UConst, UPCSafeBoxRootHash, UOrderedList,
+  UPCAccountsOrdenations,
+  UPCAbstractMemAccounts,
 {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults{$ELSE}Generics.Collections,Generics.Defaults{$ENDIF};
 
 type
@@ -34,8 +38,6 @@ type
     procedure SaveTo(const AItem: TOperationBlockExt; AIsAddingItem : Boolean; var ABytes: TBytes); override;
   end;
 
-  TPCAbstractMemListAccounts = class;
-
   TAccountNameInfo = record
     accountName: string;
     accountNumber: cardinal;
@@ -43,30 +45,22 @@ type
 
   { TPCAbstractMemListAccountNames }
 
-  TPCAbstractMemListAccountNames = class(TAbstractMemOrderedTList<TAccountNameInfo>)
+  TPCAbstractMemListAccountNames = Class(TAbstractMemBTreeData<TAccountNameInfo>)
   private
     FPCAbstractMem: TPCAbstractMem;
+    procedure LoadFrom(const ABytes: TBytes; var AItem: TAccountNameInfo);
+    procedure SaveTo(const AItem: TAccountNameInfo; var ABytes: TBytes);
+    function FindByName(const AName : String; out AAbstractMemPosition : TAbstractMemPosition) : Boolean; overload;
   protected
-    function ToString(const AItem: TAccountNameInfo): string; override;
-
-    procedure LoadFrom(const ABytes: TBytes; var AItem: TAccountNameInfo); override;
-    procedure SaveTo(const AItem: TAccountNameInfo; AIsAddingItem : Boolean; var ABytes: TBytes); override;
-    function Compare(const ALeft, ARight: TAccountNameInfo): integer; override;
+    function LoadData(const APosition : TAbstractMemPosition) : TAccountNameInfo; override;
+    function SaveData(const AData : TAccountNameInfo) : TAMZone; override;
   public
-    function IndexOf(const AName : String) : Integer;
-    procedure Remove(const AName : String);
-    procedure Add(const AName : String; AAccountNumber : Cardinal);
-    function FindByName(const AName : String; out AIndex : Integer) : Boolean;
-  end;
-
-  { TPCAbstractMemListAccounts }
-
-  TPCAbstractMemListAccounts = class(TAbstractMemTList<TAccount>)
-  private
-    FPCAbstractMem: TPCAbstractMem;
-  protected
-    procedure LoadFrom(const ABytes: TBytes; var AItem: TAccount); override;
-    procedure SaveTo(const AItem: TAccount; AIsAddingItem : Boolean; var ABytes: TBytes); override;
+    function NodeDataToString(const AData : TAbstractMemPosition) : String; override;
+    // Special
+    procedure AddNameAndNumber(const AName : String; AAccountNumber : Cardinal);
+    function FindByName(const AName : String) : Boolean; overload;
+    function FindByName(const AName : String; out ANameInfo : TAccountNameInfo) : Boolean; overload;
+    function DeleteAccountName(const AName : String) : Boolean;
   end;
 
   { TPCAbstractMemBytesBuffer32Safebox }
@@ -128,6 +122,8 @@ type
 
     FSavingOldGridCache : Boolean;
     FSavingOldDefaultCacheDataBlocksSize : Integer;
+    FAccountsOrderedByUpdatedBlock : TAccountsOrderedByUpdatedBlock;
+    FAccountsOrderedBySalePrice : TAccountsOrderedBySalePrice;
 
     function IsChecking : Boolean;
     procedure DoCheck;
@@ -145,6 +141,7 @@ type
     procedure SetSavingNewSafeboxMode(const Value: Boolean);
   protected
     procedure UpgradeAbstractMemVersion(const ACurrentHeaderVersion : Integer);
+    function DoGetAccount(AAccountNumber : Integer; var AAccount : TAccount) : Boolean;
   public
     constructor Create(const ASafeboxFileName: string; AReadOnly: boolean);
     class function AnalyzeFile(const ASafeboxFileName: string; var ABlocksCount : Integer) : Boolean;
@@ -185,6 +182,8 @@ type
     Property MaxAccountsCache : Integer read GetMaxAccountsCache write SetMaxAccountsCache;
     Property MaxAccountKeysCache : Integer read GetMaxAccountKeysCache write SetMaxAccountKeysCache;
     Property SavingNewSafeboxMode : Boolean read FSavingNewSafeboxMode write SetSavingNewSafeboxMode;
+    Property AccountsOrderedByUpdatedBlock : TAccountsOrderedByUpdatedBlock read FAccountsOrderedByUpdatedBlock;
+    Property AccountsOrderedBySalePrice : TAccountsOrderedBySalePrice read FAccountsOrderedBySalePrice;
   end;
 
 implementation
@@ -192,8 +191,8 @@ implementation
 uses UAccounts;
 
 const
-  CT_PCAbstractMem_FileVersion = CT_PROTOCOL_5;
-  CT_PCAbstractMem_HeaderVersion = 1;
+  CT_PCAbstractMem_FileVersion = 100;
+  CT_PCAbstractMem_HeaderVersion = 3;
 
 function _AccountCache_Comparision(const Left, Right: TAccountCache.PAVLCacheMemData): Integer;
 begin
@@ -260,145 +259,6 @@ begin
   end;
 end;
 
-{ TPCAbstractMemListAccounts }
-
-procedure TPCAbstractMemListAccounts.LoadFrom(const ABytes: TBytes; var AItem: TAccount);
-var
-  LPointer: TAbstractMemPosition;
-  LStream : TStream;
-  w : Word;
-begin
-  AItem.Clear;
-  LStream := TMemoryStream.Create;
-  Try
-    LPointer := 0;
-    LStream.Write(ABytes[0],Length(ABytes));
-    LStream.Position := 0;
-
-    LStream.Read( AItem.account , 4 );
-
-    LStream.Read( w,2 );
-    if (w<>CT_PROTOCOL_5) then raise EPCAbstractMem.Create(Format('Invalid Account %d protocol %d',[AItem.account,w]));
-
-    LStream.Read( w, 2 );
-    case w of
-      CT_NID_secp256k1,CT_NID_secp384r1,CT_NID_sect283k1,CT_NID_secp521r1 : Begin
-        AItem.accountInfo.state := as_Normal;
-        LStream.Read(LPointer,4);
-        AItem.accountInfo.accountKey := FPCAbstractMem.FAccountKeys.GetKeyAtPosition( LPointer );
-        if w<>AItem.accountInfo.accountKey.EC_OpenSSL_NID then raise EPCAbstractMem.Create('INCONSISTENT 20200318-2');
-      End;
-      CT_AccountInfo_ForSale, CT_AccountInfo_ForAccountSwap, CT_AccountInfo_ForCoinSwap : Begin
-        case w of
-          CT_AccountInfo_ForSale : AItem.accountInfo.state := as_ForSale;
-          CT_AccountInfo_ForAccountSwap : AItem.accountInfo.state := as_ForAtomicAccountSwap;
-          CT_AccountInfo_ForCoinSwap : AItem.accountInfo.state := as_ForAtomicCoinSwap;
-        end;
-        LStream.Read(LPointer,4);
-        AItem.accountInfo.accountKey := FPCAbstractMem.FAccountKeys.GetKeyAtPosition( LPointer );
-
-        LStream.Read(AItem.accountInfo.locked_until_block,4);
-        LStream.Read(AItem.accountInfo.price,8);
-        LStream.Read(AItem.accountInfo.account_to_pay,4);
-        LStream.Read(LPointer,4);
-        AItem.accountInfo.new_publicKey := FPCAbstractMem.FAccountKeys.GetKeyAtPosition( LPointer );
-        if (w<>CT_AccountInfo_ForSale) then begin
-          AItem.accountInfo.hashed_secret.FromSerialized(LStream);
-        end;
-
-      End;
-      else raise EPCAbstractMem.Create(Format('Unknow accountInfo type %d for account %d',[w,Aitem.account]));
-    end;
-    //
-    LStream.Read( AItem.balance , 8);
-    LStream.Read( AItem.updated_on_block_passive_mode , 4);
-    LStream.Read( AItem.updated_on_block_active_mode , 4);
-    LStream.Read( AItem.n_operation , 4);
-    AItem.name.FromSerialized( LStream );
-    LStream.Read( AItem.account_type ,2);
-    AItem.account_data.FromSerialized( LStream );
-    if AItem.account_seal.FromSerialized( LStream )<0 then raise EPCAbstractMem.Create('INCONSISTENT 20200318-4');
-    // Force account_seal to 20 bytes
-    if Length(AItem.account_seal)<>20 then begin
-      AItem.account_seal := TBaseType.T20BytesToRawBytes( TBaseType.To20Bytes(AItem.account_seal) );
-    end;
-  Finally
-    LStream.Free;
-  End;
-end;
-
-procedure TPCAbstractMemListAccounts.SaveTo(const AItem: TAccount; AIsAddingItem : Boolean; var ABytes: TBytes);
-var LStream : TStream;
-  LPointer : TAbstractMemPosition;
-  w : Word;
-  LPrevious : TAccount;
-begin
-  if (Length(ABytes)>0) and (Not AIsAddingItem) then begin
-    // Capture previous values
-    LoadFrom(ABytes,LPrevious);
-    if (LPrevious.account<>AItem.account) then raise EPCAbstractMem.Create(Format('INCONSISTENT account number %d<>%d',[AItem.account,LPrevious.account]));
-
-    if Not LPrevious.accountInfo.accountKey.IsEqualTo( AItem.accountInfo.accountKey ) then begin
-      // Remove previous account link
-      FPCAbstractMem.FAccountKeys.GetPositionOfKeyAndRemoveAccount( LPrevious.accountInfo.accountKey, LPrevious.account );
-    end;
-  end;
-
-  LStream := TMemoryStream.Create;
-  try
-    LStream.Position := 0;
-
-
-    LStream.Write( AItem.account , 4 );
-
-    w := CT_PROTOCOL_5;
-    LStream.Write( w, 2 );
-
-    w := 0;
-    case AItem.accountInfo.state of
-      as_Normal : begin
-        LPointer := FPCAbstractMem.FAccountKeys.GetPositionOfKeyAndAddAccount(AItem.accountInfo.accountKey,AItem.account);
-        LStream.Write( AItem.accountInfo.accountKey.EC_OpenSSL_NID , 2 );
-        LStream.Write( LPointer, 4);
-      end;
-      as_ForSale : w := CT_AccountInfo_ForSale;
-      as_ForAtomicAccountSwap : w := CT_AccountInfo_ForAccountSwap;
-      as_ForAtomicCoinSwap :  w := CT_AccountInfo_ForCoinSwap;
-    end;
-    if (w>0) then begin
-      LStream.Write(w,2);
-
-      LPointer := FPCAbstractMem.FAccountKeys.GetPositionOfKeyAndAddAccount(AItem.accountInfo.accountKey,AItem.account);
-      LStream.Write( LPointer, 4);
-
-      LStream.Write(AItem.accountInfo.locked_until_block,4);
-      LStream.Write(AItem.accountInfo.price,8);
-      LStream.Write(AItem.accountInfo.account_to_pay,4);
-      LPointer := FPCAbstractMem.FAccountKeys.GetPositionOfKey(AItem.accountInfo.new_publicKey,True);
-      LStream.Write(LPointer,4);
-      if (w<>CT_AccountInfo_ForSale) then begin
-        AItem.accountInfo.hashed_secret.ToSerialized(LStream);
-      end;
-    end;
-    //
-    LStream.Write( AItem.balance , 8);
-    LStream.Write( AItem.updated_on_block_passive_mode , 4);
-    LStream.Write( AItem.updated_on_block_active_mode , 4);
-    LStream.Write( AItem.n_operation , 4);
-
-    AItem.name.ToSerialized( LStream );
-
-    LStream.Write( AItem.account_type ,2);
-    AItem.account_data.ToSerialized( LStream );
-    AItem.account_seal.ToSerialized( LStream );
-    //
-    ABytes.FromStream( LStream );
-
-  finally
-    LStream.Free;
-  end;
-end;
-
 { TPCAbstractMem }
 
 function TPCAbstractMem.CheckConsistency(AReport: TStrings) : Boolean;
@@ -437,6 +297,11 @@ begin
   DoInit(LIsNew);
 end;
 
+function _TComparison_TAccountNameInfo(const ALeft, ARight : TAccountNameInfo) : Integer;
+begin
+  Result := CompareText(ALeft.accountName,ARight.accountName);
+end;
+
 function TPCAbstractMem.DoInit(out AIsNewStructure : Boolean) : Boolean;
 const
   CT_HEADER_MIN_SIZE = 100;
@@ -451,6 +316,8 @@ const
   [28..31] 4 bytes: LZoneAccountKeys.position
   [32..35] 4 bytes: FZoneAggregatedHashrate.position
   [36..39] 4 bytes: LZoneBuffersBlockHash
+  [40..43] 4 bytes: LZoneAccountsOrderedByUpdatedBlock.position
+  [44..47] 4 bytes: LZoneAccountsOrderedBySalePrice.position
   ...
   [96..99] 4 bytes: Header version
   }
@@ -458,7 +325,9 @@ var LZone,
   LZoneBlocks,
   LZoneAccounts,
   LZoneAccountsNames,
-  LZoneAccountKeys : TAMZone;
+  LZoneAccountKeys,
+  LZoneAccountsOrderedByUpdatedBlock,
+  LZoneAccountsOrderedBySalePrice : TAMZone;
   LZoneBuffersBlockHash : TAbstractMemPosition;
   LHeader, LBuffer, LBigNum : TBytes;
   LIsGood : Boolean;
@@ -472,6 +341,8 @@ begin
   FreeAndNil(FAccountsNames);
   FreeAndNil(FAccountKeys);
   FreeAndNil(FBufferBlocksHash);
+  FreeAndNil(FAccountsOrderedByUpdatedBlock);
+  FreeAndNil(FAccountsOrderedBySalePrice);
   //
   Result := False;
   AIsNewStructure := True;
@@ -482,6 +353,8 @@ begin
   LZoneAccountKeys.Clear;
   FZoneAggregatedHashrate.Clear;
   LZoneBuffersBlockHash := 0;
+  LZoneAccountsOrderedByUpdatedBlock.Clear;
+  LZoneAccountsOrderedBySalePrice.Clear;
 
   if (FAbstractMem.ReadFirstData(LZone,LHeader)) then begin
     // Check if header is valid:
@@ -504,11 +377,27 @@ begin
         Move(LHeader[28], LZoneAccountKeys.position, 4);
         Move(LHeader[32], FZoneAggregatedHashrate.position, 4);
         LZoneBuffersBlockHash := LZone.position + 36;
+        Move(LHeader[40], LZoneAccountsOrderedByUpdatedBlock.position, 4);
+        Move(LHeader[44], LZoneAccountsOrderedBySalePrice.position, 4);
+        //
         Move(LHeader[96], LHeaderVersion, 4);
         if (LHeaderVersion>CT_PCAbstractMem_HeaderVersion) then begin
           TLog.NewLog(lterror,ClassName,Format('Header version readed %d is greater than expected %d',[LHeaderVersion,CT_PCAbstractMem_HeaderVersion]));
         end else begin
           AIsNewStructure := False;
+          //
+          if (Not FAbstractMem.ReadOnly) then begin
+            if (LZoneAccountsOrderedByUpdatedBlock.position=0) then begin
+              LZoneAccountsOrderedByUpdatedBlock := FAbstractMem.New(TAbstractMemBTree.MinAbstractMemInitialPositionSize);
+              Move(LZoneAccountsOrderedByUpdatedBlock.position,LHeader[40],4);
+              FAbstractMem.Write(LZone.position,LHeader[0],Length(LHeader));
+            end;
+            if (LZoneAccountsOrderedBySalePrice.position=0) then begin
+              LZoneAccountsOrderedBySalePrice := FAbstractMem.New(TAbstractMemBTree.MinAbstractMemInitialPositionSize);
+              Move(LZoneAccountsOrderedBySalePrice.position,LHeader[44],4);
+              FAbstractMem.Write(LZone.position,LHeader[0],Length(LHeader));
+            end;
+          end;
         end;
       end;
     end;
@@ -530,6 +419,10 @@ begin
     LZoneAccountKeys := FAbstractMem.New( 100 );
     FZoneAggregatedHashrate := FAbstractMem.New(100); // Note: Enough big to store a BigNum
     LZoneBuffersBlockHash := LZone.position+36;
+    LZoneAccountsOrderedByUpdatedBlock := FAbstractMem.New(
+      TAbstractMemBTree.MinAbstractMemInitialPositionSize);
+    LZoneAccountsOrderedBySalePrice := FAbstractMem.New(
+      TAbstractMemBTree.MinAbstractMemInitialPositionSize);
 
     Move(LZoneBlocks.position,       LHeader[16],4);
     Move(LZoneAccounts.position,     LHeader[20],4);
@@ -537,6 +430,8 @@ begin
     Move(LZoneAccountKeys.position,  LHeader[28],4);
     Move(FZoneAggregatedHashrate.position,LHeader[32],4);
     LHeaderVersion := CT_PCAbstractMem_HeaderVersion;
+    Move(LZoneAccountsOrderedByUpdatedBlock, LHeader[40],4);
+    Move(LZoneAccountsOrderedBySalePrice, LHeader[44],4);
     Move(LHeaderVersion,             LHeader[96],4);
 
     FAbstractMem.Write(LZone.position,LHeader[0],Length(LHeader));
@@ -549,12 +444,12 @@ begin
   FBlocks.FPCAbstractMem := Self;
 
   FAccounts := TPCAbstractMemListAccounts.Create( FAbstractMem, LZoneAccounts, 100000, Self.UseCacheOnAbstractMemLists);
-  FAccounts.FPCAbstractMem := Self;
 
-  FAccountsNames := TPCAbstractMemListAccountNames.Create( FAbstractMem, LZoneAccountsNames, 5000 , False, Self.UseCacheOnAbstractMemLists);
+  FAccountsNames := TPCAbstractMemListAccountNames.Create( FAbstractMem, LZoneAccountsNames, False, 31, _TComparison_TAccountNameInfo);
   FAccountsNames.FPCAbstractMem := Self;
 
   FAccountKeys := TPCAbstractMemAccountKeys.Create( FAbstractMem, LZoneAccountKeys.position, Self.UseCacheOnAbstractMemLists);
+  FAccounts.AccountKeys := FAccountKeys;
 
   // Read AggregatedHashrate
   SetLength(LBuffer,100);
@@ -564,6 +459,12 @@ begin
   end;
   FBufferBlocksHash := TPCAbstractMemBytesBuffer32Safebox.Create(FAbstractMem,LZoneBuffersBlockHash,FBlocks.Count);
 
+  FAccountsOrderedByUpdatedBlock := TAccountsOrderedByUpdatedBlock.Create({$IFDEF USE_ABSTRACTMEM}FAbstractMem,LZoneAccountsOrderedByUpdatedBlock,{$ENDIF}DoGetAccount);
+  FAccounts.AccountsOrderedByUpdatedBlock := FAccountsOrderedByUpdatedBlock;
+
+  FAccountsOrderedBySalePrice := TAccountsOrderedBySalePrice.Create({$IFDEF USE_ABSTRACTMEM}FAbstractMem,LZoneAccountsOrderedBySalePrice,{$ENDIF}DoGetAccount);
+  FAccounts.AccountsOrderedBySalePrice := FAccountsOrderedBySalePrice;
+
   FAccountCache.Clear;
 
   if (Not AIsNewStructure) And (Not FAbstractMem.ReadOnly) And (LHeaderVersion<CT_PCAbstractMem_HeaderVersion) then begin
@@ -645,6 +546,8 @@ begin
   FreeAndNil(FAccountKeys);
   FreeAndNil(FBufferBlocksHash);
   FreeAndNil(FAggregatedHashrate);
+  FreeAndNil(FAccountsOrderedByUpdatedBlock);
+  FreeAndNil(FAccountsOrderedBySalePrice);
   if (FFileName<>'') And (FAbstractMem is TMem) And (Not FAbstractMem.ReadOnly) then begin
     LFile := TFileStream.Create(FFileName,fmCreate);
     try
@@ -676,6 +579,12 @@ begin
   End;
 end;
 
+function TPCAbstractMem.DoGetAccount(AAccountNumber: Integer; var AAccount: TAccount): Boolean;
+begin
+  AAccount := GetAccount(AAccountNumber);
+  Result := True;
+end;
+
 procedure TPCAbstractMem.FlushCache;
 var LBigNum : TBytes;
   Ltc : TTickCount;
@@ -684,7 +593,6 @@ begin
   Ltc := TPlatform.GetTickCount;
   FBlocks.FlushCache;
   FAccounts.FlushCache;
-  FAccountsNames.FlushCache;
   FAccountKeys.FlushCache;
   FBufferBlocksHash.Flush;
   LBigNum := FAggregatedHashrate.RawValue.ToSerialized;
@@ -743,13 +651,13 @@ begin
   if (AAccount.account<0) or (AAccount.account>FAccounts.Count) then begin
     raise EPCAbstractMem.Create(Format('Account %d not in range %d..%d',[AAccount.account,0,FAccounts.Count]));
   end;
-  FAccountCache.Remove(AAccount);
   if (AAccount.account = FAccounts.Count) then begin
     FAccounts.Add(AAccount);
   end else begin
-    FAccounts.SetItem( AAccount.account , AAccount);
+    FAccounts.Item[ AAccount.account ] := AAccount;
   end;
   // Update cache
+  FAccountCache.Remove(AAccount);
   FAccountCache.Add(AAccount);
 end;
 
@@ -903,24 +811,17 @@ procedure TPCAbstractMem.UpgradeAbstractMemVersion(const ACurrentHeaderVersion:
 var LFirstTC, LTC : TTickCount;
   i : integer;
   LAccount : TAccount;
+  LaccInfoNul : TAccountInfo;
 begin
   LFirstTC := TPlatform.GetTickCount;
   LTC := LFirstTC;
-  if (ACurrentHeaderVersion=0) then begin
-    // Redo AccountNames
-    TLog.NewLog(ltinfo,ClassName,Format('Upgrade AbstractMem file from %d to %d with %d Accounts and %d AccNames',[ACurrentHeaderVersion,CT_PCAbstractMem_HeaderVersion, AccountsCount, AccountsNames.Count]));
-    AccountsNames.Clear;
+  if (ACurrentHeaderVersion=2) then begin
+    // Set accounts price
+    LaccInfoNul.Clear;
     for i := 0 to AccountsCount-1 do begin
       LAccount := GetAccount(i);
-      if Length(LAccount.name)>0 then begin
-        AccountsNames.Add( LAccount.name.ToString, LAccount.account );
-      end;
-      if TPlatform.GetElapsedMilliseconds(LTC)>5000 then begin
-        LTC := TPlatform.GetTickCount;
-        TLog.NewLog(ltdebug,ClassName,Format('Upgrading %d/%d found %d',[i,AccountsCount,AccountsNames.Count]));
-      end;
+      AccountsOrderedBySalePrice.UpdateAccountBySalePrice(LAccount.account,LaccInfoNul,LAccount.accountInfo);
     end;
-    TLog.NewLog(ltdebug,ClassName,Format('End upgrade found %d',[AccountsNames.Count]));
   end;
   TLog.NewLog(ltinfo,ClassName,Format('Finalized upgrade AbstractMem file from %d to %d in %.2f seconds',[ACurrentHeaderVersion,CT_PCAbstractMem_HeaderVersion, TPlatform.GetElapsedMilliseconds(LFirstTC)/1000]));
 end;
@@ -1003,25 +904,25 @@ begin
   Result.Clear;
   Result.account := AAccountNumber;
   if Not FAccountCache.Find(Result,Result) then begin
-    Result := FAccounts.GetItem( AAccountNumber );
+    Result := FAccounts.Item[ AAccountNumber ];
     // Save for future usage:
     FAccountCache.Add(Result);
   end;
 end;
 
-{ TPCAbstractMemListAccountNames }
 
-function TPCAbstractMemListAccountNames.ToString(const AItem: TAccountNameInfo): string;
-begin
-  Result:= Format('AccountNameInfo: Account:%d Name(%d):%d',[AItem.accountNumber, Length(AItem.accountName), AItem.accountName]);
-end;
+{ TPCAbstractMemListAccountNames }
 
-function TPCAbstractMemListAccountNames.IndexOf(const AName: String): Integer;
-var LFind : TAccountNameInfo;
+function TPCAbstractMemListAccountNames.LoadData(const APosition: TAbstractMemPosition): TAccountNameInfo;
+var LZone : TAMZone;
+  LBytes : TBytes;
 begin
-  LFind.accountName := AName;
-  LFind.accountNumber := 0;
-  if Not Find(LFind,Result) then Result := -1;
+  if Not FPCAbstractMem.AbstractMem.GetUsedZoneInfo( APosition, False, LZone) then
+    raise EAbstractMemTList.Create(Format('%s.LoadData Inconsistency error used zone info not found at pos %d',[Self.ClassName,APosition]));
+  SetLength(LBytes,LZone.size);
+  if FPCAbstractMem.AbstractMem.Read(LZone.position, LBytes[0], Length(LBytes) )<>Length(LBytes) then
+    raise EAbstractMemTList.Create(Format('%s.LoadData Inconsistency error cannot read %d bytes at pos %d',[Self.ClassName,LZone.size,APosition]));
+  LoadFrom(LBytes,Result);
 end;
 
 procedure TPCAbstractMemListAccountNames.LoadFrom(const ABytes: TBytes; var AItem: TAccountNameInfo);
@@ -1032,14 +933,31 @@ begin
   Move(ABytes[LTmp.GetSerializedLength],AItem.accountNumber,4);
 end;
 
-procedure TPCAbstractMemListAccountNames.Remove(const AName: String);
-var i : Integer;
+function TPCAbstractMemListAccountNames.NodeDataToString(const AData: TAbstractMemPosition): String;
+var Lani : TAccountNameInfo;
+begin
+  Lani := LoadData(AData);
+  Result:= Format('AccountNameInfo: Account:%d Name(%d):%d',[Lani.accountNumber, Length(Lani.accountName), Lani.accountName]);
+end;
+
+function TPCAbstractMemListAccountNames.DeleteAccountName(const AName: String) : Boolean;
+var  Lani : TAccountNameInfo;
+begin
+  Lani.accountName := AName;
+  Lani.accountNumber := 0;
+  Result := DeleteData(Lani);
+end;
+
+function TPCAbstractMemListAccountNames.SaveData(const AData: TAccountNameInfo): TAMZone;
+var LBytes : TBytes;
 begin
-  i := IndexOf(AName);
-  if i>=0 then Delete(i);
+  SetLength(LBytes,0);
+  SaveTo(AData,LBytes);
+  Result := FPCAbstractMem.AbstractMem.New(Length(LBytes));
+  FPCAbstractMem.AbstractMem.Write(Result.position,LBytes[0],Length(LBytes));
 end;
 
-procedure TPCAbstractMemListAccountNames.SaveTo(const AItem: TAccountNameInfo; AIsAddingItem : Boolean; var ABytes: TBytes);
+procedure TPCAbstractMemListAccountNames.SaveTo(const AItem: TAccountNameInfo; var ABytes: TBytes);
 var LStream : TStream;
   LTmp : TBytes;
 begin
@@ -1055,36 +973,46 @@ begin
   End;
 end;
 
-procedure TPCAbstractMemListAccountNames.Add(const AName: String; AAccountNumber: Cardinal);
-var LItem : TAccountNameInfo;
-  i : Integer;
+procedure TPCAbstractMemListAccountNames.AddNameAndNumber(const AName: String; AAccountNumber: Cardinal);
+var Lani : TAccountNameInfo;
+  Lposition : TAbstractMemPosition;
 begin
-  LItem.accountName := AName;
-  LItem.accountNumber := AAccountNumber;
-  i := inherited Add(LItem);
-  if (i<0) then begin
-    i := IndexOf(AName);
-    if (i<0) then
+  Lani.accountName := AName;
+  Lani.accountNumber := AAccountNumber;
+  if Not AddData(Lani) then begin
+    if Not FindData(Lani,Lposition) then
       raise EPCAbstractMem.Create(Format('Fatal error Cannot add account(%d) name %s',[AAccountNumber,AName]))
     else raise EPCAbstractMem.Create(Format('Cannot add account(%d) name %s because used by %d with %s',[AAccountNumber,AName,
-      GetItem(i).accountNumber,GetItem(i).accountName]));
+      Lani.accountNumber,Lani.accountName]));
   end;
 end;
 
-function TPCAbstractMemListAccountNames.Compare(const ALeft, ARight: TAccountNameInfo): integer;
-Var LBytesLeft,LBytesRight : TBytes;
+function TPCAbstractMemListAccountNames.FindByName(const AName: String): Boolean;
+var Lpos : TAbstractMemPosition;
+begin
+  Result := FindByName(AName,Lpos);
+end;
+
+function TPCAbstractMemListAccountNames.FindByName(const AName: String; out ANameInfo: TAccountNameInfo): Boolean;
+var Lpos : TAbstractMemPosition;
 begin
-  LBytesLeft.FromString(ALeft.accountName);
-  LBytesRight.FromString(ARight.accountName);
-  Result := TBaseType.BinStrComp(LBytesLeft,LBytesRight);
+  if FindByName(AName,Lpos) then begin
+    ANameInfo := LoadData(Lpos);
+    Result := True;
+  end else begin
+    if Lpos<>0 then begin
+      ANameInfo := LoadData(Lpos);
+    end;
+    Result := False;
+  end;
 end;
 
-function TPCAbstractMemListAccountNames.FindByName(const AName: String; out AIndex: Integer): Boolean;
-var LFind : TAccountNameInfo;
+function TPCAbstractMemListAccountNames.FindByName(const AName: String; out AAbstractMemPosition: TAbstractMemPosition): Boolean;
+var Lani : TAccountNameInfo;
 begin
-  LFind.accountName := AName;
-  LFind.accountNumber := 0;
-  Result := Find(LFind,AIndex);
+  Lani.accountName := AName;
+  Lani.accountNumber := 0;
+  Result := FindData(Lani,AAbstractMemPosition);
 end;
 
 { TPCAbstractMemListBlocks }
@@ -1166,15 +1094,18 @@ procedure TPCAbstractMemCheckThread.BCExecute;
     inc(FErrorsCount);
     TLog.NewLog(ltError,ClassName,'CheckConsistency: '+AError);
   end;
-var iBlock, i, iAccName : Integer;
+var iBlock, i : Integer;
   LAccount : TAccount;
   LBlockAccount : TBlockAccount;
+  LHighestOperationBlock : TOperationBlockExt;
   LOrdered : TOrderedList<Integer>;
   LOrderedNames : TOrderedList<String>;
-  LAccountNameInfo : TAccountNameInfo;
   LTC, LTCInitial : TTickCount;
   LAggregatedHashrate, LBlockHashRate : TBigNum;
+  LBuff1,LBuff2 : TRawBytes;
+  Laninfo : TAccountNameInfo;
 begin
+  LBlockAccount := CT_BlockAccount_NUL;
   iBlock :=0;
   LOrdered := TOrderedList<Integer>.Create(False,TComparison_Integer);
   LOrderedNames := TOrderedList<String>.Create(False,TComparison_String);
@@ -1182,6 +1113,7 @@ begin
   Try
     LTC := TPlatform.GetTickCount;
     LTCInitial := LTC;
+    LHighestOperationBlock := FPCAbstractMem.GetBlockInfo(FPCAbstractMem.BlocksCount-1);
     while (iBlock < FPCAbstractMem.BlocksCount) and (Not Terminated) do begin
       if FMustRestart then begin
         TLog.NewLog(ltdebug,ClassName,Format('Restarting check thread after %d/%d blocks',[iBlock+1,FPCAbstractMem.BlocksCount]) );
@@ -1192,6 +1124,7 @@ begin
         LOrdered.Clear;
         LOrderedNames.Clear;
         LAggregatedHashrate.Value := 0;
+        LHighestOperationBlock := FPCAbstractMem.GetBlockInfo(FPCAbstractMem.BlocksCount-1);
       end;
 
       LBlockAccount := FPCAbstractMem.GetBlockAccount(iBlock);
@@ -1202,19 +1135,30 @@ begin
           if LOrderedNames.Add(LAccount.name.ToString)<0 then begin
             _error(Format('Account %d name %s allready added',[LAccount.account,LAccount.name.ToString]));
           end;
-          iAccName := FPCAbstractMem.AccountsNames.IndexOf(LAccount.name.ToString);
-          if iAccName<0 then begin
+          if Not FPCAbstractMem.AccountsNames.FindByName(LAccount.name.ToString,Laninfo) then begin
             // ERROR
             _error(Format('Account %d name %s not found at list',[LAccount.account,LAccount.name.ToString]));
           end else begin
-            if FPCAbstractMem.AccountsNames.Item[iAccName].accountNumber<>LAccount.account then begin
-              _error(Format('Account %d name %s found at list at pos %d but links to %d',[LAccount.account,LAccount.name.ToString,iAccName,FPCAbstractMem.AccountsNames.Item[iAccName].accountNumber]));
+            if Laninfo.accountNumber<>LAccount.account then begin
+              _error(Format('Account %d name %s found at list but links to %d',[LAccount.account,LAccount.name.ToString,Laninfo.accountNumber]));
             end;
             if (LOrdered.Add(LAccount.account)<0) then begin
               _error(Format('Account %d (with name %s) allready added',[LAccount.account,LAccount.name.ToString]));
             end;
           end;
         end;
+        if LAccount.GetLastUpdatedBlock>=FPCAbstractMem.BlocksCount then begin
+          _error(Format('Account Updated %d > %d - %s',[LAccount.GetLastUpdatedBlock,FPCAbstractMem.BlocksCount,TAccountComp.AccountToTxt(LAccount)]));
+        end;
+
+      end;
+      LBuff1 := TPCSafeBox.CalcBlockHash(LBlockAccount,LHighestOperationBlock.operationBlock.protocol_version);
+      If Not (LBuff1.IsEqualTo(LBlockAccount.block_hash)) then begin
+        _error(Format('Blockaccount hash for %d are not equals: calculated %s <> saved %s',[LBlockAccount.blockchainInfo.block,LBuff1.ToHexaString,LBlockAccount.block_hash.ToHexaString]));
+      end;
+      LBuff2 := FPCAbstractMem.FBufferBlocksHash.Capture((iBlock*32),32);
+      if Not LBuff1.IsEqualTo(LBuff2) then begin
+        _error(Format('Blockaccount hash for %d are not equals: %s <> %s',[LBlockAccount.blockchainInfo.block,LBuff1.ToHexaString,LBuff2.ToHexaString]));
       end;
 
       LBlockHashRate := TBigNum.TargetToHashRate( LBlockAccount.blockchainInfo.compact_target );
@@ -1231,12 +1175,21 @@ begin
       inc(iBlock);
     end;
     //
-    for i := 0 to FPCAbstractMem.AccountsNames.Count-1 do begin
-      LAccountNameInfo := FPCAbstractMem.AccountsNames.Item[i];
-      if LOrdered.IndexOf( LAccountNameInfo.accountNumber ) < 0 then begin
-        _error(Format('Account name %s at index %d/%d not found in search',[LAccountNameInfo.accountName, i+1,FPCAbstractMem.AccountsNames.Count]));
-      end;
+    FPCAbstractMem.FBufferBlocksHash.SafeBoxHashCalcType := sbh_Single_Sha256;
+    FPCAbstractMem.FBufferBlocksHash.SafeBoxHashCalcType := sbh_Merkle_Root_Hash;
+    LBuff1 := FPCAbstractMem.FBufferBlocksHash.GetSafeBoxHash;
+    FErrors.Add(Format('Last Block %d - SBH %s - Next SBH: %s',[LBlockAccount.blockchainInfo.block,LBlockAccount.blockchainInfo.initial_safe_box_hash.ToHexaString,LBuff1.ToHexaString]));
+    //
+    i := 0;
+    if FPCAbstractMem.AccountsNames.FindDataLowest(Laninfo) then begin
+      repeat
+        inc(i);
+        if LOrdered.IndexOf(Laninfo.accountNumber ) < 0 then begin
+          _error(Format('Account name %s at index %d/%d not found in search',[Laninfo.accountName, i,FPCAbstractMem.AccountsNames.Count]));
+        end;
+      until Not FPCAbstractMem.AccountsNames.FindDataSuccessor(Laninfo,Laninfo);
     end;
+
     if (LOrdered.Count)<>FPCAbstractMem.AccountsNames.Count then begin
       _error(Format('Found %d accounts with names but %d on list',[LOrdered.Count,FPCAbstractMem.AccountsNames.Count]));
     end;

+ 25 - 48
src/core/UPCAbstractMemAccountKeys.pas

@@ -10,7 +10,7 @@ uses Classes, SysUtils,
   SyncObjs,
   UAbstractMem, UFileMem, UAbstractMemTList,
   UAbstractBTree, UAbstractAVLTree,
-  UPCDataTypes, UBaseTypes, UAVLCache,
+  UPCDataTypes, UBaseTypes, UAVLCache, UAbstractMemBTree,
   {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults{$ELSE}Generics.Collections,Generics.Defaults{$ENDIF};
 
 type
@@ -31,16 +31,16 @@ type
 
   { TAccountsUsingThisKey }
 
-  TAccountsUsingThisKey = Class(TAbstractMemOrderedTList<Cardinal>)
+  TAccountsUsingThisKey = Class(TAbstractMemBTree)
+    // AbstractMem position will be considered a Account Number
   protected
-    function GetItem(index : Integer) : Cardinal; override;
-    procedure LoadFrom(const ABytes : TBytes; var AItem : Cardinal); override;
-    procedure SaveTo(const AItem : Cardinal; AIsAddingItem : Boolean; var ABytes : TBytes); override;
-    function Compare(const ALeft, ARight : Cardinal) : Integer; override;
+    procedure DisposeData(var AData : TAbstractMemPosition); override;
+    function DoCompareData(const ALeftData, ARightData: TAbstractMemPosition): Integer; override;
+  public
+    function NodeDataToString(const AData : TAbstractMemPosition) : String; override;
   public
     Constructor Create(AAbstractMem : TAbstractMem; const AInitialZone : TAMZone; AUseCache : Boolean); reintroduce;
-    Function Add(const AItem : Cardinal) : Integer; reintroduce;
-    procedure Delete(index : Integer); reintroduce;
+    function Get(Index : Integer) : Cardinal;
   End;
 
   TAccountKeyByPosition = record
@@ -269,8 +269,10 @@ begin
   try
   Lautk := GetAccountsUsingThisKey(AAccountKey);
   if Assigned(Lautk) then begin
-    for i:=0 to Lautk.Count-1 do begin
-      AList.Add( Lautk.GetItem(i) );
+    if Lautk.FindLowest(i) then begin
+      repeat
+        AList.Add(i);
+      until Not Lautk.FindSuccessor(i,i);
     end;
   end;
   finally
@@ -414,7 +416,6 @@ function TPCAbstractMemAccountKeys.GetPositionOfKeyAndRemoveAccount(
   const AAccountNumber: Cardinal): TAbstractMemPosition;
 var LNode : TAbstractMemAccountKeyNode;
   LZone : TAMZone;
-  i : Integer;
   Lacckutk : TAccountsUsingThisKey;
   LAccKeyByPos : TAccountKeyByPosition;
 begin
@@ -445,10 +446,7 @@ begin
   end;
 
   if Assigned(LAccKeyByPos.accountsUsingThisKey) then begin
-    i := LAccKeyByPos.accountsUsingThisKey.IndexOf( AAccountNumber );
-    if i>=0 then begin
-      LAccKeyByPos.accountsUsingThisKey.Delete( i );
-    end;
+    LAccKeyByPos.accountsUsingThisKey.Delete( AAccountNumber );
   end;
   finally
     FAccountKeysLock.Release;
@@ -524,53 +522,32 @@ end;
 
 { TAccountsUsingThisKey }
 
-procedure TAccountsUsingThisKey.LoadFrom(const ABytes: TBytes; var AItem: Cardinal);
-begin
-  Move(ABytes[0],AItem,4);
-end;
-
-procedure TAccountsUsingThisKey.SaveTo(const AItem: Cardinal; AIsAddingItem : Boolean; var ABytes: TBytes);
+constructor TAccountsUsingThisKey.Create(AAbstractMem: TAbstractMem; const AInitialZone: TAMZone; AUseCache : Boolean);
 begin
-  SetLength(ABytes,4);
-  Move(AItem,ABytes[0],4);
-  raise Exception.Create('INCONSISTENT 20200324-1 NEVER CALL HERE');
+  inherited Create(AAbstractMem,AInitialZone,False, 7);
 end;
 
-function TAccountsUsingThisKey.Add(const AItem: Cardinal): Integer;
-var
-  LFound : Boolean;
-  LBytes : TBytes;
-  LZone : TAMZone;
-begin
-  FList.LockList;
-  try
-    LFound := Find(AItem,Result);
-    if (LFound and AllowDuplicates) or (Not LFound) then begin
-      FList.Insert( Result , AItem );
-    end else Result := -1;
-  finally
-    FList.UnlockList;
-  end;
-end;
 
-function TAccountsUsingThisKey.Compare(const ALeft, ARight: Cardinal): Integer;
+procedure TAccountsUsingThisKey.DisposeData(var AData: TAbstractMemPosition);
 begin
-  Result := ALeft - ARight;
+  // NOTE: Nothing to do NEITHER to inherit from ancestor
 end;
 
-constructor TAccountsUsingThisKey.Create(AAbstractMem: TAbstractMem; const AInitialZone: TAMZone; AUseCache : Boolean);
+function TAccountsUsingThisKey.DoCompareData(const ALeftData, ARightData: TAbstractMemPosition): Integer;
 begin
-  inherited Create(AAbstractMem,AInitialZone,1000,False, AUseCache);
+  Result := ALeftData - ARightData;
 end;
 
-procedure TAccountsUsingThisKey.Delete(index: Integer);
+function TAccountsUsingThisKey.Get(Index: Integer): Cardinal;
+var i : Integer;
 begin
-  FList.Delete( index );
+  if Not FindIndex(Index,i) then raise Exception.Create(Format('Accounts using this key index %d not found',[Index]));
+  Result := i;
 end;
 
-function TAccountsUsingThisKey.GetItem(index: Integer): Cardinal;
+function TAccountsUsingThisKey.NodeDataToString(const AData: TAbstractMemPosition): String;
 begin
-  Result := FList.Position[index];
+  Result := IntToStr(AData);
 end;
 
 { TPCAccountKeyByPositionCache }

+ 214 - 0
src/core/UPCAbstractMemAccounts.pas

@@ -0,0 +1,214 @@
+unit UPCAbstractMemAccounts;
+
+interface
+
+{$IFDEF FPC}
+  {$MODE DELPHI}
+{$ENDIF}
+
+uses Classes, SysUtils, SyncObjs,
+  UPCAbstractMemAccountKeys, UPCAccountsOrdenations,
+  UAbstractMem,
+  UAbstractMemTList,
+  UPCDataTypes,
+  UBaseTypes,
+  UConst,
+  {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults{$ELSE}Generics.Collections,Generics.Defaults{$ENDIF};
+
+type
+  { TPCAbstractMemListAccounts }
+
+  TPCAbstractMemListAccounts = class(TAbstractMemTList<TAccount>)
+  private
+    FAccountKeys: TPCAbstractMemAccountKeys;
+    FAccountsOrderedByUpdatedBlock : TAccountsOrderedByUpdatedBlock;
+    FAccountsOrderedBySalePrice : TAccountsOrderedBySalePrice;
+  protected
+    procedure LoadFrom(const ABytes: TBytes; var AItem: TAccount); override;
+    procedure SaveTo(const AItem: TAccount; AIsAddingItem : Boolean; var ABytes: TBytes); override;
+  public
+    Constructor Create(AAbstractMem : TAbstractMem; const AInitialZone : TAMZone; ADefaultElementsPerBlock : Integer; AUseCache : Boolean); override;
+    class procedure LoadAccountFromTBytes(const ABytes: TBytes; const AAccountKeys : TPCAbstractMemAccountKeys; var AItem: TAccount);
+    property AccountKeys: TPCAbstractMemAccountKeys read FAccountKeys write FAccountKeys;
+    property AccountsOrderedByUpdatedBlock: TAccountsOrderedByUpdatedBlock read FAccountsOrderedByUpdatedBlock write FAccountsOrderedByUpdatedBlock;
+    property AccountsOrderedBySalePrice: TAccountsOrderedBySalePrice read FAccountsOrderedBySalePrice write FAccountsOrderedBySalePrice;
+  end;
+
+  EAbsctractMemAccounts = Class(Exception);
+
+implementation
+
+uses UAccounts;
+
+{ TPCAbstractMemListAccounts }
+
+constructor TPCAbstractMemListAccounts.Create(AAbstractMem: TAbstractMem;
+  const AInitialZone: TAMZone; ADefaultElementsPerBlock: Integer;
+  AUseCache: Boolean);
+begin
+  inherited;
+  FAccountKeys := Nil;
+  FAccountsOrderedByUpdatedBlock := Nil;
+  FAccountsOrderedBySalePrice := Nil;
+end;
+
+class procedure TPCAbstractMemListAccounts.LoadAccountFromTBytes(
+  const ABytes: TBytes; const AAccountKeys: TPCAbstractMemAccountKeys;
+  var AItem: TAccount);
+var
+  LPointer: TAbstractMemPosition;
+  LStream : TStream;
+  w : Word;
+begin
+  AItem.Clear;
+  LStream := TMemoryStream.Create;
+  Try
+    LPointer := 0;
+    LStream.Write(ABytes[0],Length(ABytes));
+    LStream.Position := 0;
+
+    LStream.Read( AItem.account , 4 );
+
+    LStream.Read( w,2 );
+    if (w<>CT_PROTOCOL_5) then raise EAbsctractMemAccounts.Create(Format('Invalid Account %d protocol %d',[AItem.account,w]));
+
+    LStream.Read( w, 2 );
+    case w of
+      CT_NID_secp256k1,CT_NID_secp384r1,CT_NID_sect283k1,CT_NID_secp521r1 : Begin
+        AItem.accountInfo.state := as_Normal;
+        LStream.Read(LPointer,4);
+        if Assigned(AAccountKeys) then begin
+          AItem.accountInfo.accountKey := AAccountKeys.GetKeyAtPosition( LPointer );
+          if w<>AItem.accountInfo.accountKey.EC_OpenSSL_NID then raise EAbsctractMemAccounts.Create('INCONSISTENT 20200318-2');
+        end;
+      End;
+      CT_AccountInfo_ForSale, CT_AccountInfo_ForAccountSwap, CT_AccountInfo_ForCoinSwap : Begin
+        case w of
+          CT_AccountInfo_ForSale : AItem.accountInfo.state := as_ForSale;
+          CT_AccountInfo_ForAccountSwap : AItem.accountInfo.state := as_ForAtomicAccountSwap;
+          CT_AccountInfo_ForCoinSwap : AItem.accountInfo.state := as_ForAtomicCoinSwap;
+        end;
+        LStream.Read(LPointer,4);
+        if Assigned(AAccountKeys) then begin
+          AItem.accountInfo.accountKey := AAccountKeys.GetKeyAtPosition( LPointer );
+        end;
+
+        LStream.Read(AItem.accountInfo.locked_until_block,4);
+        LStream.Read(AItem.accountInfo.price,8);
+        LStream.Read(AItem.accountInfo.account_to_pay,4);
+        LStream.Read(LPointer,4);
+        if Assigned(AAccountKeys) then begin
+          AItem.accountInfo.new_publicKey := AAccountKeys.GetKeyAtPosition( LPointer );
+        end;
+        if (w<>CT_AccountInfo_ForSale) then begin
+          AItem.accountInfo.hashed_secret.FromSerialized(LStream);
+        end;
+
+      End;
+      else raise EAbsctractMemAccounts.Create(Format('Unknow accountInfo type %d for account %d',[w,Aitem.account]));
+    end;
+    //
+    LStream.Read( AItem.balance , 8);
+    LStream.Read( AItem.updated_on_block_passive_mode , 4);
+    LStream.Read( AItem.updated_on_block_active_mode , 4);
+    LStream.Read( AItem.n_operation , 4);
+    AItem.name.FromSerialized( LStream );
+    LStream.Read( AItem.account_type ,2);
+    AItem.account_data.FromSerialized( LStream );
+    if AItem.account_seal.FromSerialized( LStream )<0 then raise EAbsctractMemAccounts.Create('INCONSISTENT 20200318-4');
+    // Force account_seal to 20 bytes
+    if Length(AItem.account_seal)<>20 then begin
+      AItem.account_seal := TBaseType.T20BytesToRawBytes( TBaseType.To20Bytes(AItem.account_seal) );
+    end;
+  Finally
+    LStream.Free;
+  End;
+end;
+
+procedure TPCAbstractMemListAccounts.LoadFrom(const ABytes: TBytes; var AItem: TAccount);
+begin
+  LoadAccountFromTBytes(ABytes,FAccountKeys,AItem);
+end;
+
+procedure TPCAbstractMemListAccounts.SaveTo(const AItem: TAccount; AIsAddingItem : Boolean; var ABytes: TBytes);
+var LStream : TStream;
+  LPointer : TAbstractMemPosition;
+  w : Word;
+  LPrevious : TAccount;
+begin
+  if (Length(ABytes)>0) and (Not AIsAddingItem) then begin
+    // Capture previous values
+    LoadFrom(ABytes,LPrevious);
+    if (LPrevious.account<>AItem.account) then raise EAbsctractMemAccounts.Create(Format('INCONSISTENT account number %d<>%d',[AItem.account,LPrevious.account]));
+
+    if Not LPrevious.accountInfo.accountKey.IsEqualTo( AItem.accountInfo.accountKey ) then begin
+      // Remove previous account link
+      FAccountKeys.GetPositionOfKeyAndRemoveAccount( LPrevious.accountInfo.accountKey, LPrevious.account );
+    end;
+
+    if LPrevious.updated_on_block_active_mode<>AItem.updated_on_block_active_mode then begin
+      FAccountsOrderedByUpdatedBlock.Update(AItem.account,LPrevious.updated_on_block_active_mode,AItem.updated_on_block_active_mode);
+    end;
+    FAccountsOrderedBySalePrice.UpdateAccountBySalePrice(AItem.account,LPrevious.accountInfo,AItem.accountInfo);
+  end else begin
+    FAccountsOrderedByUpdatedBlock.Update(AItem.account,0,AItem.updated_on_block_active_mode);
+    FAccountsOrderedBySalePrice.UpdateAccountBySalePrice(AItem.account,CT_AccountInfo_NUL,AItem.accountInfo);
+  end;
+
+  LStream := TMemoryStream.Create;
+  try
+    LStream.Position := 0;
+
+
+    LStream.Write( AItem.account , 4 );
+
+    w := CT_PROTOCOL_5;
+    LStream.Write( w, 2 );
+
+    w := 0;
+    case AItem.accountInfo.state of
+      as_Normal : begin
+        LPointer := FAccountKeys.GetPositionOfKeyAndAddAccount(AItem.accountInfo.accountKey,AItem.account);
+        LStream.Write( AItem.accountInfo.accountKey.EC_OpenSSL_NID , 2 );
+        LStream.Write( LPointer, 4);
+      end;
+      as_ForSale : w := CT_AccountInfo_ForSale;
+      as_ForAtomicAccountSwap : w := CT_AccountInfo_ForAccountSwap;
+      as_ForAtomicCoinSwap :  w := CT_AccountInfo_ForCoinSwap;
+    end;
+    if (w>0) then begin
+      LStream.Write(w,2);
+
+      LPointer := FAccountKeys.GetPositionOfKeyAndAddAccount(AItem.accountInfo.accountKey,AItem.account);
+      LStream.Write( LPointer, 4);
+
+      LStream.Write(AItem.accountInfo.locked_until_block,4);
+      LStream.Write(AItem.accountInfo.price,8);
+      LStream.Write(AItem.accountInfo.account_to_pay,4);
+      LPointer := FAccountKeys.GetPositionOfKey(AItem.accountInfo.new_publicKey,True);
+      LStream.Write(LPointer,4);
+      if (w<>CT_AccountInfo_ForSale) then begin
+        AItem.accountInfo.hashed_secret.ToSerialized(LStream);
+      end;
+    end;
+    //
+    LStream.Write( AItem.balance , 8);
+    LStream.Write( AItem.updated_on_block_passive_mode , 4);
+    LStream.Write( AItem.updated_on_block_active_mode , 4);
+    LStream.Write( AItem.n_operation , 4);
+
+    AItem.name.ToSerialized( LStream );
+
+    LStream.Write( AItem.account_type ,2);
+    AItem.account_data.ToSerialized( LStream );
+    AItem.account_seal.ToSerialized( LStream );
+    //
+    ABytes.FromStream( LStream );
+
+  finally
+    LStream.Free;
+  end;
+end;
+
+
+end.

+ 245 - 0
src/core/UPCAccountsOrdenations.pas

@@ -0,0 +1,245 @@
+unit UPCAccountsOrdenations;
+
+{ Copyright (c) 2016-2021 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+interface
+
+{$IFDEF FPC}
+  {$MODE DELPHI}
+{$ENDIF}
+
+{$I ./../config.inc}
+
+uses Classes, SysUtils,
+  UAbstractMem,
+  UAbstractMemBTree,
+  UAbstractBTree,
+  UPCDataTypes, UBaseTypes,
+  {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults{$ELSE}Generics.Collections,Generics.Defaults{$ENDIF};
+
+type
+
+  TCallReturnAccount = function(AAccountNumber : Integer; var AAccount : TAccount) : Boolean of object;
+
+  TAccountsOrderedByUpdatedBlock = Class
+  private
+    type
+      TAccounstByUpdatedBlockBTree = Class({$IFDEF USE_ABSTRACTMEM}TAbstractMemBTree{$ELSE}TMemoryBTree<Integer>{$ENDIF})
+      protected
+        FCallReturnAccount : TCallReturnAccount;
+        FSearching_AccountNumber : Integer;
+        FSearching_UpdatedBlock : Integer;
+        function DoCompareData(const ALeftData, ARightData: TAbstractMemPosition): Integer; override;
+      public
+        function NodeDataToString(const AData : TAbstractMemPosition) : String; override;
+      End;
+  private
+    var
+    FBTree : TAccounstByUpdatedBlockBTree;
+    {$IFDEF USE_ABSTRACTMEM}
+    FAbstractMem : TAbstractMem;
+    {$ENDIF}
+  public
+    constructor Create({$IFDEF USE_ABSTRACTMEM}AAbstractMem : TAbstractMem; const AInitialZone : TAMZone; {$ENDIF}ACallReturnAccount : TCallReturnAccount);
+    destructor Destroy; override;
+    function First(var AAccountNumber : Integer) : Boolean;
+    function Next(var AAccountNumber : Integer) : Boolean;
+    function Count : Integer;
+    function Update(const AAccountNumber, AOldUpdatedBlock, ANewUpdatedBlock : Integer) : Boolean;
+  End;
+
+  TAccountsOrderedBySalePrice = Class({$IFDEF USE_ABSTRACTMEM}TAbstractMemBTree{$ELSE}TMemoryBTree<Integer>{$ENDIF})
+  protected
+    FCallReturnAccount : TCallReturnAccount;
+    FSearching_AccountNumber : Integer;
+    FSearching_AccountInfo : TAccountInfo;
+    function DoCompareData(const ALeftData, ARightData: TAbstractMemPosition): Integer; override;
+  public
+    function NodeDataToString(const AData : TAbstractMemPosition) : String; override;
+    function UpdateAccountBySalePrice(const AAccountNumber : Integer; const AOldAccountInfo, ANewAccountInfo : TAccountInfo) : Boolean;
+    constructor Create({$IFDEF USE_ABSTRACTMEM}AAbstractMem : TAbstractMem; const AInitialZone: TAMZone; {$ENDIF}ACallReturnAccount : TCallReturnAccount);
+  End;
+
+implementation
+
+Uses UPCAbstractMemAccounts, UAccounts;
+
+{ TAccountsOrderedByUpdatedBlock }
+
+function TAccountsOrderedByUpdatedBlock.Count: Integer;
+begin
+  Result := FBTree.Count;
+end;
+
+constructor TAccountsOrderedByUpdatedBlock.Create({$IFDEF USE_ABSTRACTMEM}AAbstractMem : TAbstractMem; const AInitialZone : TAMZone; {$ENDIF}ACallReturnAccount : TCallReturnAccount);
+begin
+  {$IFDEF USE_ABSTRACTMEM}
+  FAbstractMem := AAbstractMem;
+  FBTree := TAccounstByUpdatedBlockBTree.Create(FAbstractMem,AInitialZone,False,31);
+  {$ELSE}
+  FBTree := TAccounstByUpdatedBlockBTree.Create(Nil,False,31);
+  {$ENDIF}
+  FBTree.FSearching_AccountNumber := -1;
+  FBTree.FSearching_UpdatedBlock := 0;
+  FBTree.FCallReturnAccount := ACallReturnAccount;
+end;
+
+destructor TAccountsOrderedByUpdatedBlock.Destroy;
+begin
+  FBTree.Free;
+  inherited;
+end;
+
+function TAccountsOrderedByUpdatedBlock.First(var AAccountNumber : Integer) : Boolean;
+begin
+  FBTree.Lock;
+  Try
+    FBTree.FSearching_AccountNumber := -1;
+    Result := FBTree.FindLowest(AAccountNumber);
+  Finally
+    FBTree.Unlock;
+  End;
+end;
+
+function TAccountsOrderedByUpdatedBlock.Next(var AAccountNumber : Integer): Boolean;
+begin
+  FBTree.Lock;
+  Try
+    FBTree.FSearching_AccountNumber := -1;
+    Result := FBTree.FindSuccessor(AAccountNumber,AAccountNumber);
+  Finally
+    FBTree.Unlock;
+  End;
+end;
+
+function TAccountsOrderedByUpdatedBlock.Update(const AAccountNumber, AOldUpdatedBlock, ANewUpdatedBlock: Integer): Boolean;
+var
+  Lampos : TAbstractMemPosition;
+  Lnode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode;
+  LAccount : TAccount;
+  LPosition : TAbstractMemPosition;
+  LiPos : Integer;
+begin
+  FBTree.Lock;
+  Try
+    FBTree.FSearching_AccountNumber := AAccountNumber;
+    FBTree.FSearching_UpdatedBlock := AOldUpdatedBlock;
+    FBTree.Delete(AAccountNumber);
+    FBTree.FSearching_UpdatedBlock := ANewUpdatedBlock;
+    FBTree.Add(AAccountNumber);
+  Finally
+    FBTree.Unlock;
+  End;
+  Result := True;
+end;
+
+{ TAccountsOrderedByUpdatedBlock.TAccounstByUpdatedBlockBTree }
+
+function TAccountsOrderedByUpdatedBlock.TAccounstByUpdatedBlockBTree.DoCompareData(
+  const ALeftData, ARightData: TAbstractMemPosition): Integer;
+var LLeftAccount, LRightAccount : TAccount;
+begin
+  if (ALeftData = ARightData) then Exit(0);
+
+  FCallReturnAccount(ARightData,LRightAccount);
+  if ((FSearching_AccountNumber>=0) And (ALeftData=FSearching_AccountNumber)) then begin
+    Result := FSearching_UpdatedBlock - LRightAccount.updated_on_block_active_mode;
+  end else begin
+    FCallReturnAccount(ALeftData,LLeftAccount);
+    Result := LLeftAccount.updated_on_block_active_mode - LRightAccount.updated_on_block_active_mode;
+  end;
+  if Result=0 then Result := ALeftData - ARightData;
+end;
+
+function TAccountsOrderedByUpdatedBlock.TAccounstByUpdatedBlockBTree.NodeDataToString(
+  const AData: TAbstractMemPosition): String;
+var LAccount : TAccount;
+begin
+  if FCallReturnAccount(AData,LAccount) then begin
+    Result := Format('(Acc:%d Upd:%d)',[LAccount.account,LAccount.updated_on_block_active_mode]);
+  end else Result := Format('(Pos:%d not found)',[AData]);
+end;
+
+{ TAccounstBySalePrice }
+
+constructor TAccountsOrderedBySalePrice.Create({$IFDEF USE_ABSTRACTMEM}AAbstractMem: TAbstractMem;
+  const AInitialZone: TAMZone; {$ENDIF}ACallReturnAccount: TCallReturnAccount);
+begin
+  {$IFDEF USE_ABSTRACTMEM}
+  inherited Create(AAbstractMem,AInitialZone,False,15);
+  {$ELSE}
+  inherited Create(Nil,False,15);
+  {$ENDIF}
+  FCallReturnAccount := ACallReturnAccount;
+  FSearching_AccountNumber := -1;
+  FSearching_AccountInfo.Clear;
+end;
+
+function TAccountsOrderedBySalePrice.DoCompareData(const ALeftData,
+  ARightData: TAbstractMemPosition): Integer;
+var LLeftAccount, LRightAccount : TAccount;
+  LopResult : Int64;
+begin
+  if (ALeftData = ARightData) then Exit(0);
+
+  FCallReturnAccount(ARightData,LRightAccount);
+  if ((FSearching_AccountNumber>=0) And (ALeftData=FSearching_AccountNumber)) then begin
+    LopResult := FSearching_AccountInfo.price - LRightAccount.accountInfo.price;
+  end else begin
+    FCallReturnAccount(ALeftData,LLeftAccount);
+    LopResult := LLeftAccount.accountInfo.price - LRightAccount.accountInfo.price;
+  end;
+  if LopResult<0 then Result := -1
+  else if LopResult>0 then Result := 1
+  else Result := ALeftData - ARightData;
+end;
+
+function TAccountsOrderedBySalePrice.NodeDataToString(
+  const AData: TAbstractMemPosition): String;
+var LAccount : TAccount;
+begin
+  if FCallReturnAccount(AData,LAccount) then begin
+    Result := Format('(Acc:%d price:%s)',[LAccount.account,TAccountComp.FormatMoney(LAccount.accountInfo.price)]);
+  end else Result := Format('(Pos:%d not found)',[AData]);
+end;
+
+function TAccountsOrderedBySalePrice.UpdateAccountBySalePrice(const AAccountNumber: Integer;
+  const AOldAccountInfo, ANewAccountInfo: TAccountInfo): Boolean;
+var Ldone : Boolean;
+begin
+  if (TAccountComp.IsAccountForSale(AOldAccountInfo)=TAccountComp.IsAccountForSale(ANewAccountInfo)) and
+    (AOldAccountInfo.price = ANewAccountInfo.price) then Exit(True); // No updates, no need to change
+  Lock;
+  Try
+    FSearching_AccountNumber := AAccountNumber;
+    FSearching_AccountInfo := AOldAccountInfo;
+    Ldone := Delete(AAccountNumber);
+    if (Ldone) and (Not TAccountComp.IsAccountForSale(AOldAccountInfo)) then raise EAbsctractMemAccounts.Create('ERROR DEV 20210126-1');
+    if (Not Ldone) and (TAccountComp.IsAccountForSale(AOldAccountInfo)) then raise EAbsctractMemAccounts.Create('ERROR DEV 20210126-2');
+    FSearching_AccountInfo := ANewAccountInfo;
+    if (TAccountComp.IsAccountForSale(ANewAccountInfo)) then begin
+      if Not Add(AAccountNumber) then raise EAbsctractMemAccounts.Create('ERROR DEV 20210126-3');
+    end;
+    FSearching_AccountNumber := -1;
+    FSearching_AccountInfo.Clear;
+  Finally
+    Unlock;
+  End;
+  Result := True;
+end;
+
+end.

+ 32 - 0
src/core/UPCOrderedLists.pas

@@ -56,6 +56,8 @@ Type
     Procedure Disable;
     Procedure Enable;
     Function ToArray : TCardinalsArray;
+    function FillList(AStartIndex, ACount : Integer; const AList : TList<Cardinal>) : Integer; overload;
+    function FillList(AStartIndex, ACount : Integer; const AList : TList<Integer>) : Integer; overload;
   End;
 
 
@@ -180,6 +182,36 @@ begin
   if (FDisabledsCount=0) And (FModifiedWhileDisabled) then NotifyChanged;
 end;
 
+function TOrderedCardinalList.FillList(AStartIndex, ACount : Integer; const AList : TList<Cardinal>) : Integer;
+var i : Integer;
+begin
+  AList.Clear;
+  AList.Capacity := ACount;
+  if (AStartIndex=0) and (ACount=FOrderedList.Count) then begin
+    AList.InsertRange(AStartIndex,FOrderedList);
+  end else begin
+    while (ACount>0) and (AStartIndex < FOrderedList.Count) do begin
+      AList.Add( FOrderedList.Items[AStartIndex] );
+      Inc(AStartIndex);
+      Dec(ACount);
+    end;
+  end;
+  Result := AList.Count;
+end;
+
+function TOrderedCardinalList.FillList(AStartIndex, ACount: Integer; const AList: TList<Integer>): Integer;
+var i : Integer;
+begin
+  AList.Clear;
+  AList.Capacity := ACount;
+  while (ACount>0) and (AStartIndex < FOrderedList.Count) do begin
+    AList.Add( FOrderedList.Items[AStartIndex] );
+    Inc(AStartIndex);
+    Dec(ACount);
+  end;
+  Result := AList.Count;
+end;
+
 function TOrderedCardinalList.Find(const Value: Cardinal; var Index: Integer): Boolean;
 var L, H, I: Integer;
   C : Int64;

+ 180 - 0
src/core/UPCRPCFileUtils.pas

@@ -0,0 +1,180 @@
+unit UPCRPCFileUtils;
+
+{ Copyright (c) 2020 by PascalCoin developers, orignal code by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+interface
+
+{$I ./../config.inc}
+
+Uses classes, SysUtils,
+  UJSONFunctions, URPC, UCrypto, ULog,
+  {$IFDEF USE_ABSTRACTMEM}
+  UPCAbstractMem, UPCAbstractMemAccountKeys,
+  {$ENDIF}
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
+  UBlockChain, UPCOrderedLists;
+
+
+Type
+
+  { TRPCFileUtils }
+
+  TRPCFileUtils = Class
+  private
+  public
+    class function SaveAsSafeboxStream(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+    class function GenerateNewAbstractMemSafebox(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+    class function AbstractMemStats(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+
+  End;
+
+implementation
+
+uses UPCDataTypes, UFileStorage, UNode;
+
+{ TRPCFileUtils }
+
+class function TRPCFileUtils.GenerateNewAbstractMemSafebox(
+  const ASender: TRPCProcess; const AMethodName: String; AInputParams,
+  AJSONResponse: TPCJSONObject; var AErrorNum: Integer;
+  var AErrorDesc: String): Boolean;
+{$IFDEF USE_ABSTRACTMEM}
+var
+  LFileName : String;
+{$ENDIF}
+begin
+  if Not ASender.RPCServer.AllowUsePrivateKeys then begin
+    AErrorNum := CT_RPC_ErrNum_NotAllowedCall;
+    Exit(False);
+  end;
+{$IFDEF USE_ABSTRACTMEM}
+  LFileName := AInputParams.AsString('filename', '').Trim;
+  if (LFileName='') then begin
+    LFileName := TFileStorage.GetSafeboxCheckpointingFileName(TFileStorage(TNode.Node.Bank.Storage).DatabaseFolder,TNode.Node.Bank.BlocksCount);
+  end;
+  TNode.Node.Bank.SafeBox.SaveCheckpointing(LFileName);
+  AJSONResponse.GetAsObject('result').GetAsVariant('filename').Value := LFileName;
+  AErrorNum := 0;
+  AErrorDesc := '';
+  Result := True;
+{$ELSE}
+  AErrorNum := CT_RPC_ErrNum_NotImplemented;
+  AErrorDesc := 'AbstractMem library is not available in this build';
+  Result := False;
+{$ENDIF}
+end;
+
+class function TRPCFileUtils.AbstractMemStats(const ASender: TRPCProcess;
+  const AMethodName: String; AInputParams, AJSONResponse: TPCJSONObject;
+  var AErrorNum: Integer; var AErrorDesc: String): Boolean;
+var LStrings, LReport : TStrings;
+   LTotalUsedSize, LTotalUsedBlocksCount, LTotalLeaksSize, LTotalLeaksBlocksCount : Integer;
+   i, nMax : Integer;
+   Lobj : TPCJSONObject;
+   Larray : TPCJSONArray;
+begin
+  if Not ASender.RPCServer.AllowUsePrivateKeys then begin
+    AErrorNum := CT_RPC_ErrNum_NotAllowedCall;
+    Exit(False);
+  end;
+{$IFDEF USE_ABSTRACTMEM}
+  LStrings := TStringList.Create;
+  Try
+    if AInputParams.GetAsVariant('report').AsBoolean(False) then LReport := LStrings
+    else LReport := Nil;
+    Lobj := AJSONResponse.GetAsObject('result').GetAsObject('abstractmem');
+    if TNode.Node.Bank.SafeBox.PCAbstractMem.AbstractMem.CheckConsistency(LReport, LTotalUsedSize, LTotalUsedBlocksCount, LTotalLeaksSize, LTotalLeaksBlocksCount) then begin
+      Lobj.GetAsVariant('checkconsistency').Value := True;
+    end else begin
+      Lobj.GetAsVariant('checkconsistency').Value := False;
+    end;
+    Lobj.GetAsVariant('total_used_size').Value := LTotalUsedSize;
+    Lobj.GetAsVariant('total_used_blocks_count').Value := LTotalUsedBlocksCount;
+    Lobj.GetAsVariant('total_leaks_size').Value := LTotalLeaksSize;
+    Lobj.GetAsVariant('total_leaks_blocks_count').Value := LTotalLeaksBlocksCount;
+
+    if Assigned(LReport) then begin
+      Larray := Lobj.GetAsArray('report');
+      i := AInputParams.GetAsVariant('report_start').AsInteger(0);
+      nMax := AInputParams.GetAsVariant('report_max').AsInteger(100);
+      while (nMax>0) and (i>=0) and (i<LStrings.Count-1) do begin
+        Larray.GetAsVariant(Larray.Count).Value := LStrings[i];
+        inc(i);
+        dec(nMax);
+      end;
+    end;
+    Result := True;
+  Finally
+    LStrings.Free;
+  end;
+{$ELSE}
+  AErrorNum := CT_RPC_ErrNum_NotImplemented;
+  AErrorDesc := 'AbstractMem library is not available in this build';
+  Result := False;
+{$ENDIF}
+  //
+end;
+
+class function TRPCFileUtils.SaveAsSafeboxStream(const ASender: TRPCProcess;
+  const AMethodName: String; AInputParams, AJSONResponse: TPCJSONObject;
+  var AErrorNum: Integer; var AErrorDesc: String): Boolean;
+
+var LFileName : String;
+  LFs : TFileStream;
+  LStart,LEnd : Integer;
+begin
+  if Not ASender.RPCServer.AllowUsePrivateKeys then begin
+    AErrorNum := CT_RPC_ErrNum_NotAllowedCall;
+    Exit(False);
+  end;
+
+  LFileName := AInputParams.AsString('filename', '').Trim;
+  if (LFileName='') then begin
+    LFileName := TFileStorage.GetSafeboxCheckpointingFileName(TFileStorage(TNode.Node.Bank.Storage).DatabaseFolder,TNode.Node.Bank.BlocksCount);
+    LFileName := ChangeFileExt(LFileName,'.safebox');
+  end;
+  LFs := TFileStream.Create(LFileName,fmCreate);
+  try
+    LFs.Size := 0;
+    LFs.Position := 0;
+    LStart := 0;
+    LEnd := TNode.Node.Bank.BlocksCount-1;
+    TNode.Node.Bank.SafeBox.SaveSafeBoxToAStream(LFs,LStart,LEnd);
+  finally
+    LFs.Free;
+  end;
+  AJSONResponse.GetAsObject('result').GetAsVariant('filename').Value := LFileName;
+  AJSONResponse.GetAsObject('result').GetAsVariant('start').Value := LStart;
+  AJSONResponse.GetAsObject('result').GetAsVariant('end').Value := LEnd;
+  AErrorNum := 0;
+  AErrorDesc := '';
+  Result := True;
+end;
+
+initialization
+  TRPCProcess.RegisterProcessMethod('save-safebox-stream',TRPCFileUtils.SaveAsSafeboxStream);
+  TRPCProcess.RegisterProcessMethod('save-safebox-abstractmem',TRPCFileUtils.GenerateNewAbstractMemSafebox);
+  TRPCProcess.RegisterProcessMethod('abstractmem-stats',TRPCFileUtils.AbstractMemStats);
+finalization
+  TRPCProcess.UnregisterProcessMethod('save-safebox-stream');
+  TRPCProcess.UnregisterProcessMethod('save-safebox-abstractmem');
+  TRPCProcess.UnregisterProcessMethod('abstractmem-stats');
+end.

+ 24 - 12
src/core/UPCRPCFindAccounts.pas

@@ -152,6 +152,7 @@ var
   LAccPubKey : TAccountKey;
   LOutput : TPCJSONArray;
   LStartsWith : TOrderedRawList;
+  LAccountsList : TList<Integer>;
 begin
   // Get Parameters
   Result := False;
@@ -277,21 +278,32 @@ begin
     end;
   end else begin
     // Search by type-forSale-balance
-    i := LStart;
-    while (Not ASender.Terminated) And (i < LEnd) do begin
-      if (LSearchByPubkey) then begin
-        if (i>=LAccountsNumbersList.Count) then Break;
-        LAccount := ASender.Node.GetMempoolAccount( LAccountsNumbersList.Get(i) );
-      end else begin
-        LAccount := ASender.Node.GetMempoolAccount(i);
+    if (LSearchByPubkey) then begin
+      LAccountsList := TList<Integer>.Create;
+      try
+        LAccountsNumbersList.FillList(LStart,LEnd-LStart+1,LAccountsList);
+        for i := 0 to LAccountsList.Count-1 do begin
+          LAccount := ASender.Node.GetMempoolAccount( LAccountsList[i] );
+          if (_IsValidAccount(LAccount)) then begin
+            TPascalCoinJSONComp.FillAccountObject(LAccount,LOutput.GetAsObject(LOutput.Count));
+            if LOutput.Count>=LMax then break;
+          end;
+        end;
+      finally
+        LAccountsList.Free;
       end;
+    end else begin
+      i := LStart;
+      while (Not ASender.Terminated) And (i < LEnd) do begin
+        LAccount := ASender.Node.GetMempoolAccount(i);
 
-      if (_IsValidAccount(LAccount)) then begin
-        TPascalCoinJSONComp.FillAccountObject(LAccount,LOutput.GetAsObject(LOutput.Count));
-        if LOutput.Count>=LMax then break;
-      end;
-      inc(i);
+        if (_IsValidAccount(LAccount)) then begin
+          TPascalCoinJSONComp.FillAccountObject(LAccount,LOutput.GetAsObject(LOutput.Count));
+          if LOutput.Count>=LMax then break;
+        end;
+        inc(i);
 
+      end;
     end;
   end;
   Result := True;

+ 105 - 32
src/core/UPCRPCOpData.pas

@@ -27,7 +27,7 @@ interface
 Uses classes, SysUtils,
   UJSONFunctions, UAccounts, UBaseTypes, UOpTransaction, UConst, UPCDataTypes,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
-  URPC, UCrypto, UWallet, UBlockChain, ULog;
+  URPC, UCrypto, UEPasa, UWallet, UBlockChain, ULog;
 
 
 Type
@@ -132,7 +132,28 @@ class function TRPCOpData.OpData_FindOpDataOperations(
         end;
       end;
 
-    var LOpComp : TPCOperationsComp;
+      function DoAddOpData(AOpData : TOpData; var AFoundCounter : Integer; out AOperationResume : TOperationResume) : Boolean;
+      begin
+        Result := False;
+        // Search by filter:
+        if ((Not ASearchBySender) Or (ASearchSender = AOpData.Data.account_sender))
+           and ((Not ASearchByTarget) Or (ASearchTarget = AOpData.Data.account_target))
+           and ((Not ASearchByGUID) Or (EqualGUIDs(ASearchGUID,AOpData.Data.guid)))
+           and ((Not ASearchByDataSequence) Or (ASearchDataSequence = AOpData.Data.dataSequence))
+           and ((Not ASearchByDataType) Or (ASearchDataType = AOpData.Data.dataType))
+        then begin
+          if (AFoundCounter>=AStartOperation) And (AFoundCounter<=AEndOperation) then begin
+            If TPCOperation.OperationToOperationResume(ABlock_number,AOpData,False,AOpData.SignerAccount,AOperationResume) then begin
+              AOperationResume.Balance:=-1;
+              Result := True;
+            end;
+          end;
+          inc(AFoundCounter);
+        end;
+      end; // For LList...
+
+
+    var LOpComp, LMemPool : TPCOperationsComp;
       LOperation : TPCOperation;
       LOpData : TOpData;
       LOperationResume : TOperationResume;
@@ -148,6 +169,39 @@ class function TRPCOpData.OpData_FindOpDataOperations(
       Try
         LList := TList<Cardinal>.Create;
         try
+          // Mempool
+          if ABlock_number>=ASender.Node.Bank.BlocksCount then begin
+            // Search mempool
+            LMemPool := ASender.Node.LockMempoolRead;
+            try
+              LMemPool.OperationsHashTree.GetOperationsAffectingAccount(ASearchedAccount_number,LList);
+              LFound_in_block := False;
+              // Reverse order:
+              for i := LList.Count - 1 downto 0 do begin
+                LOperation := LMemPool.Operation[LList.Items[i]];
+                if LOperation is TOpData then begin
+                  //
+                  LOpData := TOpData( LOperation );
+                  if DoAddOpData(LOpData,LFoundCounter,LOperationResume) then begin
+                    LOperationResume.NOpInsideBlock:=LList.Items[i];
+                    LOperationResume.Block:=ASender.Node.Bank.BlocksCount;
+                    AOperationsResumeList.Add(LOperationResume);
+                    LFound_in_block := True;
+                  end;
+                end;
+              end; // For LList...
+              If (Not LFound_in_block) And (AFirst_Block_Is_Unknown) then begin
+                ABlock_number := ASender.Node.Bank.BlocksCount - 1;
+              end else begin
+                ABlock_number := LMemPool.PreviousUpdatedBlocks.GetPreviousUpdatedBlock(ASearchedAccount_number,ASender.Node.Bank.BlocksCount - 1);
+              end;
+            finally
+              ASender.Node.UnlockMempoolRead;
+            end;
+          end;
+
+
+
           LLast_block_number := ABlock_number+1;
           while (LLast_block_number>ABlock_number) And (AAct_depth>0)
             And (ABlock_number >= (ASearchedAccount_number DIV CT_AccountsPerBlock))
@@ -171,22 +225,11 @@ class function TRPCOpData.OpData_FindOpDataOperations(
               if LOperation is TOpData then begin
                 //
                 LOpData := TOpData( LOperation );
-                // Search by filter:
-                if ((Not ASearchBySender) Or (ASearchSender = LOpData.Data.account_sender))
-                   and ((Not ASearchByTarget) Or (ASearchTarget = LOpData.Data.account_target))
-                   and ((Not ASearchByGUID) Or (EqualGUIDs(ASearchGUID,LOpData.Data.guid)))
-                   and ((Not ASearchByDataSequence) Or (ASearchDataSequence = LOpData.Data.dataSequence))
-                   and ((Not ASearchByDataType) Or (ASearchDataType = LOpData.Data.dataType))
-                then begin
-                  if (LFoundCounter>=AStartOperation) And (LFoundCounter<=AEndOperation) then begin
-                    If TPCOperation.OperationToOperationResume(ABlock_number,LOpData,False,LOpData.SignerAccount,LOperationResume) then begin
-                      LOperationResume.Balance:=-1;
-                      LOperationResume.NOpInsideBlock:=LList.Items[i];
-                      LOperationResume.Block:=ABlock_number;
-                      AOperationsResumeList.Add(LOperationResume);
-                    end;
-                  end;
-                  inc(LFoundCounter);
+                if DoAddOpData(LOpData,LFoundCounter,LOperationResume) then begin
+                  LOperationResume.NOpInsideBlock:=LList.Items[i];
+                  LOperationResume.Block:=ABlock_number;
+                  AOperationsResumeList.Add(LOperationResume);
+                  LFound_in_block := True;
                 end;
               end;
             end; // For LList...
@@ -225,8 +268,12 @@ Var LAccount : TAccount;
 begin
   Result := False;
 
-  LSender := AInputParams.AsCardinal('sender',CT_MaxAccount);
-  LTarget := AInputParams.AsCardinal('target',CT_MaxAccount);
+  if Not TPascalCoinJSONComp.CaptureAccountNumber(AInputParams,'sender',ASender.Node,LSender,AErrorDesc) then begin
+    LSender := CT_MaxAccount;
+  end;
+  if Not TPascalCoinJSONComp.CaptureAccountNumber(AInputParams,'target',ASender.Node,LTarget,AErrorDesc) then begin
+    LTarget := CT_MaxAccount;
+  end;
   LSearchedAccount_number := CT_MaxAccount;
   LSearchBySender := (LSender>=0) And (LSender<ASender.Node.Bank.AccountsCount);
   LSearchByTarget := (LTarget>=0) And (LTarget<ASender.Node.Bank.AccountsCount);
@@ -267,7 +314,7 @@ begin
   LStartOperation := AInputParams.AsInteger('start',0);
   LMaxOperations := AInputParams.AsInteger('max',100);
   if AInputParams.IndexOfName('startblock')>=0 then begin
-    LStartBlock := AInputParams.AsInteger('startblock',100);
+    LStartBlock := AInputParams.AsInteger('startblock',ASender.Node.Bank.BlocksCount);
     LFirst_Block_Is_Unknown := True;
   end else begin
     if not ASender.RPCServer.GetMempoolAccount(LSearchedAccount_number,LAccount) then begin
@@ -277,14 +324,13 @@ begin
     end;
     LFirst_Block_Is_Unknown := False;
     LStartBlock := LAccount.GetLastUpdatedBlock;
-    if LStartBlock>=ASender.Node.Bank.BlocksCount then Dec(LStartBlock); // If its updated on mempool, don't look the mempool
   end;
 
   LOperationsResumeList := TOperationsResumeList.Create;
   try
     DoFindFromBlock(LStartBlock,
       LSearchedAccount_number,
-      LStartOperation, LStartOperation + LMaxOperations,
+      LStartOperation, LStartOperation + LMaxOperations - 1,
       LMaxDepth, LFirst_Block_Is_Unknown,
       LSearchBySender, LSender,
       LSearchByTarget, LTarget,
@@ -297,7 +343,9 @@ begin
     LResultArray := AJSONResponse.GetAsArray('result');
 
     for i := 0 to LOperationsResumeList.Count-1 do begin
-      TPascalCoinJSONComp.FillOperationObject(LOperationsResumeList.OperationResume[i],ASender.Node.Bank.BlocksCount,LResultArray.GetAsObject( LResultArray.Count ));
+      TPascalCoinJSONComp.FillOperationObject(LOperationsResumeList.OperationResume[i],ASender.Node.Bank.BlocksCount,
+        ASender.Node,ASender.RPCServer.WalletKeys,ASender.RPCServer.PayloadPasswords,
+        LResultArray.GetAsObject( LResultArray.Count ));
     end;
     Result := True;
   finally
@@ -313,28 +361,46 @@ var LOpData : TOpData;
   LOperationPayload : TOperationPayload;
   LErrors : String;
   LOPR : TOperationResume;
+  LTargetEPASA : TEPasa;
+  LTargetKey : TAccountKey;
+  LTargetRequiresPurchase : Boolean;
 begin
   Result := False;
-  if Not ASender.RPCServer.GetMempoolAccount(AInputParams.AsInteger('sender',CT_MaxAccount),LSender) then begin
+  ASender.Node.OperationSequenceLock.Acquire;  // Use lock to prevent N_Operation race-condition on concurrent operations
+  try
+
+  if Not TPascalCoinJSONComp.CaptureMempoolAccount(AInputParams,'sender',ASender.Node,LSender,AErrorDesc) then begin
     AErrorNum := CT_RPC_ErrNum_InvalidAccount;
-    AErrorDesc := 'Invalid "sender"';
     Exit;
   end;
   if (AInputParams.IndexOfName('signer')>=0) then begin
-    if Not ASender.RPCServer.GetMempoolAccount(AInputParams.AsInteger('signer',CT_MaxAccount),LSigner) then begin
+    if Not TPascalCoinJSONComp.CaptureMempoolAccount(AInputParams,'signer',ASender.Node,LSigner,AErrorDesc) then begin
       AErrorNum := CT_RPC_ErrNum_InvalidAccount;
-      AErrorDesc := 'Invalid "signer"';
       Exit;
     end;
   end else LSigner := LSender; // If no "signer" param, then use "sender" as signer by default
-  if Not ASender.RPCServer.GetMempoolAccount(AInputParams.AsInteger('target',CT_MaxAccount),LTarget) then begin
+
+  LTarget := CT_Account_NUL;
+  if Not TPascalCoinJSONComp.CaptureEPASA(AInputParams,'target',ASender.Node, LTargetEPASA, LTarget.account, LTargetKey, LTargetRequiresPurchase, AErrorDesc) then begin
     AErrorNum := CT_RPC_ErrNum_InvalidAccount;
-    AErrorDesc := 'Invalid "target"';
-    Exit;
+    Exit(False);
+  end else begin
+    LTarget := ASender.Node.GetMempoolAccount(LTarget.account);
+    if (LTargetRequiresPurchase) then begin
+      AErrorNum := CT_RPC_ErrNum_InvalidEPASA;
+      AErrorDesc := 'PayToKey not available as a EPasa format on this method';
+      Exit(False);
+    end;
+  end;
+  if Not TPascalCoinJSONComp.OverridePayloadParams(AInputParams, LTargetEPASA) then begin
+    AErrorNum := CT_RPC_ErrNum_AmbiguousPayload;
+    AErrorDesc := 'Target EPASA payload conflicts with argument payload.';
+    Exit(False);
   end;
 
   if not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
     TCrypto.HexaToRaw(AInputParams.AsString('payload','')),
+    [ptNonDeterministic],
     AInputParams.AsString('payload_method','none'),
     AInputParams.AsString('pwd',''),
     LSender.accountInfo.accountKey,
@@ -361,11 +427,17 @@ begin
       Exit;
     end;
     TPCOperation.OperationToOperationResume(0,LOpData,False,LSender.account,LOPR);
-    TPascalCoinJSONComp.FillOperationObject(LOPR,ASender.Node.Bank.BlocksCount,AJSONResponse.GetAsObject('result'));
+    TPascalCoinJSONComp.FillOperationObject(LOPR,ASender.Node.Bank.BlocksCount,
+      ASender.Node,ASender.RPCServer.WalletKeys,ASender.RPCServer.PayloadPasswords,
+      AJSONResponse.GetAsObject('result'));
     Result := True;
   finally
     LOpData.free;
   end;
+
+  finally
+    ASender.Node.OperationSequenceLock.Release;
+  end;
 end;
 
 class function TRPCOpData.OpData_SignOpData(const ASender: TRPCProcess;
@@ -409,6 +481,7 @@ begin
 
     if not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
       TCrypto.HexaToRaw(AInputParams.AsString('payload','')),
+      [ptNonDeterministic],
       AInputParams.AsString('payload_method','dest'),
       AInputParams.AsString('pwd',''),
       LPayloadPubkey,

+ 362 - 0
src/core/UPCRPCSend.pas

@@ -0,0 +1,362 @@
+unit UPCRPCSend;
+
+{ Copyright (c) 2021 by PascalCoin developers, orignal code by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+interface
+
+{$I ./../config.inc}
+
+Uses classes, SysUtils,
+  UJSONFunctions, UAccounts, UBaseTypes, UOpTransaction, UConst,
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
+  URPC, UCrypto, UWallet, UBlockChain, UEPasa, ULog, UPCOrderedLists, UPCDataTypes;
+
+
+Type
+  TRPCSend = Class
+  private
+  public
+    class function CreateOperationTransaction(const ARPCProcess : TRPCProcess; ACurrentProtocol : Word; ASender, ATarget : TAccount; AAmount, AFee : UInt64; const ARawPayload : TRawBytes; const APayloadMethod, AEncodePwd : String; const APayloadType : TPayloadType; var AErrorNum: Integer; var AErrorDesc: String) : TOpTransaction;
+    class function CreatePayToKeyTransaction(const ARPCProcess : TRPCProcess; ACurrentProtocol: Word; ASender, APurchaseAccount : TAccount; const ANewKey : TAccountKey; AAmount, AFee: UInt64; const ARawPayload: TRawBytes;  const APayloadMethod, AEncodePwd: String; const APayloadType : TPayloadType; var AErrorNum: Integer; var AErrorDesc: String) : TOpTransaction;
+    class function SendTo(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+    class function SignSendTo(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+  End;
+
+implementation
+
+{ TRPCSend }
+
+class function TRPCSend.CreateOperationTransaction(const ARPCProcess : TRPCProcess; ACurrentProtocol : Word; ASender, ATarget : TAccount; AAmount, AFee : UInt64; const ARawPayload : TRawBytes; const APayloadMethod, AEncodePwd : String; const APayloadType : TPayloadType; var AErrorNum: Integer; var AErrorDesc: String): TOpTransaction;
+var
+  LOpPayload : TOperationPayload;
+  LPrivateKey : TECPrivateKey;
+Begin
+  Result := Nil;
+  if Not ARPCProcess.RPCServer.CheckAndGetPrivateKeyInWallet(ASender.accountInfo.accountKey, LPrivateKey, AErrorNum, AErrorDesc) then Exit(Nil);
+  if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(ARawPayload, APayloadType, APayloadMethod, AEncodePwd, ASender.accountInfo.accountKey, ATarget.accountInfo.accountKey, LOpPayload, AErrorNum, AErrorDesc) then Exit(Nil);
+  Result := TOpTransaction.CreateTransaction(ACurrentProtocol, ASender.account, ASender.n_operation+1, ATarget.account, LPrivateKey, AAmount, AFee, LOpPayload);
+  if Not Result.HasValidSignature then begin
+    FreeAndNil(Result);
+    AErrorNum:=CT_RPC_ErrNum_InternalError;
+    AErrorDesc:='Invalid signature';
+    exit;
+  end;
+end;
+
+class function TRPCSend.CreatePayToKeyTransaction(const ARPCProcess : TRPCProcess; ACurrentProtocol: Word; ASender, APurchaseAccount : TAccount; const ANewKey : TAccountKey; AAmount, AFee: UInt64; const ARawPayload: TRawBytes;  const APayloadMethod, AEncodePwd: String; const APayloadType : TPayloadType; var AErrorNum: Integer; var AErrorDesc: String): TOpTransaction;
+Var
+  LOpPayload : TOperationPayload;
+  LPrivateKey : TECPrivateKey;
+Begin
+  Result := Nil;
+  if (AAmount < APurchaseAccount.accountInfo.price) then begin
+    AErrorNum := CT_RPC_ErrNum_InternalError;
+    AErrorDesc := 'Insufficient funds to purchase account for pay-to-key transaction';
+    Exit(Nil);
+  end;
+
+  if Not ARPCProcess.RPCServer.CheckAndGetPrivateKeyInWallet(ASender.accountInfo.accountKey, LPrivateKey, AErrorNum, AErrorDesc) then Exit(Nil);
+  if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(ARawPayload, APayloadType, APayloadMethod, AEncodePwd, ASender.accountInfo.accountKey, ANewKey, LOpPayload, AErrorNum, AErrorDesc) then Exit(Nil);
+  Result := TOpBuyAccount.CreateBuy(ACurrentProtocol, ASender.account, ASender.n_operation + 1, APurchaseAccount.account, APurchaseAccount.accountInfo.account_to_pay, APurchaseAccount.accountInfo.price, AAmount, AFee, ANewKey, LPrivateKey, LOpPayload);
+  if Not Result.HasValidSignature then begin
+    FreeAndNil(Result);
+    AErrorNum:=CT_RPC_ErrNum_InternalError;
+    AErrorDesc:='Invalid signature';
+    exit;
+  end;
+end;
+
+class function TRPCSend.SendTo(const ASender: TRPCProcess;
+  const AMethodName: String; AInputParams, AJSONResponse: TPCJSONObject;
+  var AErrorNum: Integer; var AErrorDesc: String): Boolean;
+
+{ JSON-RPC "sendto"
+
+### sendto
+Executes a transaction operation from "sender" to "target"
+
+##### Params
+- `sender` : Integer - Sender account
+- `target` : Integer - Destination account
+- `amount` : PASCURRENCY - Coins to be transferred
+- `fee` : PASCURRENCY - Fee of the operation
+- `payload` : HEXASTRING - Payload "item" that will be included in this operation
+- `payload_method` : String - Encode type of the item payload
+  - `none` : Not encoded. Will be visible for everybody
+  - `dest` (default) : Using Public key of "target" account. Only "target" will be able to decrypt this payload
+  - `sender` : Using sender Public key. Only "sender" will be able to decrypt this payload
+  - `aes` : Encrypted data using `pwd` param
+- `pwd` : String - Used to encrypt payload with `aes` as a `payload_method`. If none equals to empty password
+
+##### Result
+If transaction is successfull will return a JSON Object in "[Operation Object]" format.
+Otherwise, will return a JSON-RPC error code with description
+
+}
+
+var
+ LSender, LTarget : TAccount;
+ LTargetEPASA : TEPasa;
+ LTargetKey : TAccountKey;
+ LTargetRequiresPurchase : Boolean;
+ LAmount, LFee : UInt64;
+ LRawPayload : TRawBytes;
+ LPayload_method, LEncodePwd, LErrors : String;
+ LOpt : TOpTransaction;
+ LOpr : TOperationResume;
+ LTmpTarget : Cardinal;
+begin
+  // Get Parameters
+  Result := False;
+
+  if (Not ASender.RPCServer.AllowUsePrivateKeys) then begin
+    // Protection when server is locked to avoid private keys call
+    AErrorNum := CT_RPC_ErrNum_NotAllowedCall;
+    Exit;
+  end;
+  If Not ASender.RPCServer.WalletKeys.IsValidPassword then begin
+    AErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
+    AErrorDesc := 'Wallet is password protected. Unlock first';
+    exit;
+  end;
+
+  // Do new operation
+  ASender.Node.OperationSequenceLock.Acquire;  // Use lock to prevent N_Operation race-condition on concurrent sends
+  try
+
+    if Not TPascalCoinJSONComp.CaptureAccountNumber(AInputParams,'sender',ASender.Node,LSender.account,AErrorDesc) then begin
+      AErrorNum := CT_RPC_ErrNum_InvalidAccount;
+      Exit;
+    end else LSender := ASender.Node.GetMempoolAccount(LSender.account);
+
+    LTarget := CT_Account_NUL;
+    if Not TPascalCoinJSONComp.CaptureEPASA(AInputParams,'target',ASender.Node, LTargetEPASA, LTarget.account, LTargetKey, LTargetRequiresPurchase, AErrorDesc) then begin
+      AErrorNum := CT_RPC_ErrNum_InvalidAccount;
+      Exit;
+    end else LTarget := ASender.Node.GetMempoolAccount(LTarget.account);
+
+    if (LTargetEPASA.PayloadType.ToProtocolValue=0) and ((LTarget.account=0) or (LTarget.account=LSender.account)) then begin
+      // Try to decode from payload
+      // String payload:
+      if TPascalCoinJSONComp.CaptureEPASA(AInputParams,'payload',ASender.Node, LTargetEPASA, LTmpTarget, LTargetKey, LTargetRequiresPurchase, AErrorDesc) then begin
+        if LTargetRequiresPurchase then begin
+          AInputParams.GetAsVariant('payload').Value := LTargetEPASA.GetRawPayloadBytes.ToHexaString;
+          LTarget := ASender.Node.GetMempoolAccount(LTmpTarget);
+        end;
+      end;
+      if (Not LTargetRequiresPurchase) then begin
+        // HEXASTRING payload:
+        if (TPascalCoinJSONComp.CaptureEPASA(TCrypto.HexaToRaw(AInputParams.AsString('payload','')).ToPrintable, ASender.Node, LTargetEPASA, LTmpTarget, LTargetKey, LTargetRequiresPurchase, AErrorDesc)) then begin
+          if LTargetRequiresPurchase then begin
+            AInputParams.GetAsVariant('payload').Value := LTargetEPASA.GetRawPayloadBytes.ToHexaString;
+            LTarget := ASender.Node.GetMempoolAccount(LTmpTarget);
+          end;
+        end;
+      end;
+    end;
+
+    if Not TPascalCoinJSONComp.OverridePayloadParams(AInputParams, LTargetEPASA) then begin
+      AErrorNum := CT_RPC_ErrNum_AmbiguousPayload;
+      AErrorDesc := 'Target EPASA payload conflicts with argument payload.';
+      Exit;
+    end;
+
+    LAmount := TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('amount',0));
+    LFee := TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('fee',0));
+    LRawPayload := TCrypto.HexaToRaw(AInputParams.AsString('payload',''));
+    LPayload_method := AInputParams.AsString('payload_method','dest');
+    LEncodePwd := AInputParams.AsString('pwd','');
+
+    // Create operation
+    if LTargetRequiresPurchase then begin
+      // Buy Account
+      LOpt := CreatePayToKeyTransaction(
+          ASender, ASender.Node.Bank.SafeBox.CurrentProtocol,
+          LSender, LTarget, LTargetKey, LAmount, LFee,
+          LRawPayload, LPayload_method, LEncodePwd, LTargetEPASA.PayloadType,
+          AErrorNum, AErrorDesc);
+    end else begin
+      // Transaction
+      LOpt := CreateOperationTransaction(
+        ASender, ASender.Node.Bank.SafeBox.CurrentProtocol,
+        LSender, LTarget, LAmount, LFee,
+        LRawPayload, LPayload_method, LEncodePwd, LTargetEPASA.PayloadType,
+        AErrorNum, AErrorDesc);
+    end;
+    // Execute operation
+    if Assigned(LOpt) then
+    try
+      If not ASender.Node.AddOperation(Nil,LOpt,LErrors) then begin
+        AErrorDesc := 'Error adding operation: '+LErrors;
+        AErrorNum := CT_RPC_ErrNum_InvalidOperation;
+        Exit;
+      end;
+      TPCOperation.OperationToOperationResume(0,LOpt,False,LSender.account,LOpr);
+      TPascalCoinJSONComp.FillOperationObject(LOpr,ASender.Node.Bank.BlocksCount,
+        ASender.Node,ASender.RPCServer.WalletKeys,ASender.RPCServer.PayloadPasswords,
+        AJSONResponse.GetAsObject('result'));
+      Result := true;
+    finally
+      LOpt.free;
+    end;
+  finally
+    ASender.Node.OperationSequenceLock.Release;
+  end;
+end;
+
+class function TRPCSend.SignSendTo(const ASender: TRPCProcess;
+  const AMethodName: String; AInputParams, AJSONResponse: TPCJSONObject;
+  var AErrorNum: Integer; var AErrorDesc: String): Boolean;
+
+{ JSON-RPC "signsendto"
+
+### signsendto
+
+Creates and signs a "Send to" operation without checking information and without transfering to the network.
+It's usefull for "cold wallets" that are off-line (not synchronized with the network) and only holds private keys
+
+##### Params
+- `rawoperations` : HEXASTRING (optional) - If we want to add a sign operation with other previous operations, here we must put previous `rawoperations` result
+- `sender` : Integer - Sender account
+- `target` : Integer - Target account
+- `sender_enc_pubkey` or `sender_b58_pubkey` : HEXASTRING - Public key (in encoded format or b58 format) of the sender account
+- `target_enc_pubkey` or `target_b58_pubkey` : HEXASTRING - Public key (in encoded format or b58 format) of the target account
+- `last_n_operation` : Last value of `n_operation` obtained with an [Account object](#account-object), for example when called to [getaccount](#getaccount)
+- `amount`,`fee`,`payload`,`payload_method`,`pwd` : Same values that calling [sendto](#sendto)
+
+##### Result
+
+Wallet must be unlocked and sender private key (searched with provided public key) must be in wallet.
+No other checks are made (no checks for valid target, valid n_operation, valid amount or fee ...)
+Returns a [Raw Operations Object](#raw-operations-object)
+
+}
+var
+ LSender, LTarget : TAccount;
+ LTargetEPASA : TEPasa;
+ LTargetKey : TAccountKey;
+ LTargetRequiresPurchase : Boolean;
+ LHexaStringOperationsHashTree, LErrors : String;
+ LProtocol : Integer;
+ LOperationsHashTree : TOperationsHashTree;
+ LOpt : TOpTransaction;
+ LOpr : TOperationResume;
+ LRawPayload : TRawBytes;
+ LPayload_method, LEncodePwd : String;
+ LAmount, LFee : UInt64;
+begin
+  Result := False;
+
+  if (Not ASender.RPCServer.AllowUsePrivateKeys) then begin
+    // Protection when server is locked to avoid private keys call
+    AErrorNum := CT_RPC_ErrNum_NotAllowedCall;
+    Exit;
+  end;
+  If Not ASender.RPCServer.WalletKeys.IsValidPassword then begin
+    AErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
+    AErrorDesc := 'Wallet is password protected. Unlock first';
+    exit;
+  end;
+  if Not TPascalCoinJSONComp.CaptureAccountNumber(AInputParams,'sender',Nil,LSender.account,AErrorDesc) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidAccount;
+    Exit;
+  end;
+  if Not TPascalCoinJSONComp.CaptureNOperation(AInputParams,'last_n_operation',Nil,LSender.n_operation,AErrorDesc) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidAccount;
+    Exit;
+  end;
+
+  if Not TPascalCoinJSONComp.CaptureEPASA(AInputParams,'target', nil, LTargetEPASA, LTarget.account, LTargetKey, LTargetRequiresPurchase, AErrorDesc) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidEPASA;
+    Exit;
+  end;
+
+  if Not TPascalCoinJSONComp.OverridePayloadParams(AInputParams, LTargetEPASA) then begin
+    AErrorNum := CT_RPC_ErrNum_AmbiguousPayload;
+    Exit;
+  end;
+
+  If Not TPascalCoinJSONComp.CapturePubKey(AInputParams,'sender_',LSender.accountInfo.accountKey,AErrorDesc) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidPubKey;
+    exit;
+  end;
+
+  If Not TPascalCoinJSONComp.CapturePubKey(AInputParams,'target_',LTarget.accountInfo.accountKey,AErrorDesc) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidPubKey;
+    exit;
+  end;
+
+  LAmount := TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('amount',0));
+  LFee := TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('fee',0));
+  LRawPayload := TCrypto.HexaToRaw(AInputParams.AsString('payload',''));
+  LPayload_method := AInputParams.AsString('payload_method','dest');
+  LEncodePwd := AInputParams.AsString('pwd','');
+
+  LHexaStringOperationsHashTree := AInputParams.AsString('rawoperations','');
+  LProtocol := AInputParams.AsCardinal('protocol',CT_BUILD_PROTOCOL);
+
+  if Not TPascalCoinJSONComp.HexaStringToOperationsHashTree(LHexaStringOperationsHashTree,LProtocol,LOperationsHashTree,LErrors) then begin
+    AErrorNum:=CT_RPC_ErrNum_InvalidData;
+    AErrorDesc:= 'Error decoding param "rawoperations": '+LErrors;
+    Exit;
+  end;
+
+
+  Try
+    // Create operation
+    if LTargetRequiresPurchase then begin
+      // Buy Account
+      LOpt := CreatePayToKeyTransaction(
+          ASender, ASender.Node.Bank.SafeBox.CurrentProtocol,
+          LSender, LTarget, LTargetKey, LAmount, LFee,
+          LRawPayload, LPayload_method, LEncodePwd, LTargetEPASA.PayloadType,
+          AErrorNum, AErrorDesc);
+    end else begin
+      // Transaction
+      LOpt := CreateOperationTransaction(
+        ASender, ASender.Node.Bank.SafeBox.CurrentProtocol,
+        LSender, LTarget, LAmount, LFee,
+        LRawPayload, LPayload_method, LEncodePwd, LTargetEPASA.PayloadType,
+        AErrorNum, AErrorDesc);
+    end;
+
+    // Execute operation
+    if Assigned(LOpt) then
+    try
+      LOperationsHashTree.AddOperationToHashTree(LOpt);
+      TPascalCoinJSONComp.FillOperationsHashTreeObject(LOperationsHashTree,AJSONResponse.GetAsObject('result'));
+      Result := true;
+    finally
+      LOpt.Free;
+    end;
+
+  Finally
+    LOperationsHashTree.Free;
+  End;
+end;
+
+initialization
+  TRPCProcess.RegisterProcessMethod('signsendto',TRPCSend.SignSendTo);
+  TRPCProcess.RegisterProcessMethod('sendto',TRPCSend.SendTo);
+finalization
+  TRPCProcess.UnregisterProcessMethod('signsendto');
+  TRPCProcess.UnregisterProcessMethod('sendto');
+end.

+ 66 - 28
src/core/UPCTNetDataExtraMessages.pas

@@ -33,7 +33,7 @@ interface
 {$ENDIF}
 
 Uses Classes, UThread, UAccounts, UBlockChain, UNetProtocol, SysUtils, UNode,
-  UWallet, UNetProtection, UPCDataTypes,
+  UWallet, UNetProtection, UPCDataTypes, UPCAccountsOrdenations, UOrderedList,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 type
@@ -42,9 +42,9 @@ type
     FNode : TNode;
     FNetData : TNetData;
     FWalletKeys : TWalletKeysExt;
-    function DoAskForFreeAccount(ANewPubliKey : TAccountKey) : Integer;
-    {$IFDEF TESTNET}
+    function DoAskForFreeAccount(const ANewPubliKey : TAccountKey; const AMessage : String) : Integer;
     procedure DoGiveMeAnAccount(ANetData : TNetData; ASenderConnection : TNetConnection; const AHeaderData : TNetHeaderData; AReceivedData : TStream; AResponseData : TStream);
+    {$IFDEF TESTNET}
     procedure DoGiveMeMoney(ANetData : TNetData; ASenderConnection : TNetConnection; const AHeaderData : TNetHeaderData; AReceivedData : TStream; AResponseData : TStream);
     {$ENDIF}
     procedure OnTNetDataProcessReservedAreaMessage(ANetData : TNetData; ASenderConnection : TNetConnection; const AHeaderData : TNetHeaderData; AReceivedData : TStream; AResponseData : TStream);
@@ -54,7 +54,7 @@ type
     class function InitNetDataExtraMessages(ANode : TNode; ANetData : TNetData; AWalletKeys : TWalletKeysExt) : TPCTNetDataExtraMessages;
     constructor Create(ANode : TNode; ANetData : TNetData; AWalletKeys : TWalletKeysExt);
     destructor Destroy; override;
-    class function AskForFreeAccount(ANewPubliKey : TAccountKey) : Integer;
+    class function AskForFreeAccount(const ANewPubliKey : TAccountKey; const AMessage : String): Integer;
   End;
 
 const
@@ -63,16 +63,16 @@ const
 
 implementation
 
-Uses UOpTransaction, UBaseTypes, ULog;
+Uses UOpTransaction, UBaseTypes, ULog, UPCAbstractMemAccountKeys;
 
 var _PCTNetDataExtraMessages : TPCTNetDataExtraMessages = Nil;
 
 { TPCTNetDataExtraMessages }
 
-class function TPCTNetDataExtraMessages.AskForFreeAccount(ANewPubliKey : TAccountKey): Integer;
+class function TPCTNetDataExtraMessages.AskForFreeAccount(const ANewPubliKey : TAccountKey; const AMessage : String): Integer;
 begin
   if Assigned(_PCTNetDataExtraMessages) then begin
-    Result := _PCTNetDataExtraMessages.DoAskForFreeAccount(ANewPubliKey);
+    Result := _PCTNetDataExtraMessages.DoAskForFreeAccount(ANewPubliKey,AMessage);
   end else Result := 0;
 end;
 
@@ -98,15 +98,18 @@ begin
   inherited;
 end;
 
-function TPCTNetDataExtraMessages.DoAskForFreeAccount(ANewPubliKey : TAccountKey): Integer;
+function TPCTNetDataExtraMessages.DoAskForFreeAccount(const ANewPubliKey : TAccountKey; const AMessage : String): Integer;
 var i : Integer;
   LNetConnection : TNetConnection;
   LRequestStream : TMemoryStream;
+  Lraw : TRawBytes;
 begin
   Result := 0;
   LRequestStream := TMemoryStream.Create;
   try
     TStreamOp.WriteAccountKey(LRequestStream,ANewPubliKey);
+    Lraw.FromString(AMessage);
+    TStreamOp.WriteAnsiString(LRequestStream,Lraw);
     LRequestStream.position := 0;
     for i := 0 to FNetData.ConnectionsCountAll-1 do begin
       LNetConnection := FNetData.Connection(i);
@@ -126,42 +129,76 @@ begin
   end;
 end;
 
-{$IFDEF TESTNET}
 procedure TPCTNetDataExtraMessages.DoGiveMeAnAccount(ANetData: TNetData;
   ASenderConnection: TNetConnection; const AHeaderData: TNetHeaderData;
   AReceivedData, AResponseData: TStream);
 var LSenderPublicKey : TAccountKey;
-  LIndexKey : Integer;
+  LIndexKey,LOnSafebox,LOnMempool : Integer;
   LAccount : TAccount;
-  LOpChangeKey : TOpChangeKey;
+  LOpRecoverFounds : TOpRecoverFounds;
   LPayload : TOperationPayload;
-  LErrors : String;
+  LErrors, LSenderMessage : String;
   LWord : Word;
+  LAccOrd : TAccountsOrderedByUpdatedBlock;
+  LRaw : TRawBytes;
 begin
   if Not (AHeaderData.header_type in [ntp_request,ntp_autosend]) then Exit; // Nothing to do
   // Protection to allow spam
-  if ANetData.IpInfos.Update_And_ReachesLimits(ASenderConnection.Client.RemoteHost,'EXTRA','GIVE_ME_AN_ACCOUNT',AHeaderData.buffer_data_length,True,
+  if ANetData.IpInfos.Update_And_ReachesLimits(ASenderConnection.Client.RemoteHost,'EXTRA','GIVE_ME_AN_ACCOUNT',
+    AHeaderData.buffer_data_length,True,
     TArray<TLimitLifetime>.Create(TLimitLifetime.Create(300,2,20000))) then Exit;
   // Read info
   if TStreamOp.ReadAccountKey(AReceivedData,LSenderPublicKey)<=0 then Exit;
-  if Not RandomGetWalletKeysAccount(FNode.Bank.SafeBox,FWalletKeys,0,10000,LIndexKey,LAccount) then Exit;
-  // Send
-  LPayload := CT_TOperationPayload_NUL;
-  LPayload.payload_raw.FromString('Free Account to '+ASenderConnection.Client.RemoteHost);
-  LOpChangeKey := TOpChangeKey.Create(FNode.Bank.SafeBox.CurrentProtocol,LAccount.account,LAccount.n_operation+1,
-    LAccount.account,FWalletKeys.Key[LIndexKey].PrivateKey,LSenderPublicKey,0,LPayload);
+  if TStreamOp.ReadString(AReceivedData,LSenderMessage)<0 then Exit;
+
+  if FNode.GetAccountsAvailableByPublicKey(LSenderPublicKey,LOnSafebox,LOnMempool)>0 then begin
+    // Exit;
+    TLog.NewLog(ltdebug,ClassName,Format('Not Sending to %s because PublicKey %s is used %d and mempool %d',[ASenderConnection.Client.RemoteHost,
+      TAccountComp.AccountPublicKeyExport(LSenderPublicKey),LOnSafebox,LOnMempool]));
+    Lword := 0;
+    AResponseData.Write(Lword,2);
+    Exit;
+  end;
+
+  LAccOrd := FNode.Bank.SafeBox.AccountsOrderedByUpdatedBlock;
+  if Assigned(LAccOrd) then begin
+    LAccount := CT_Account_NUL;
+    if LAccOrd.First(LIndexKey) then begin
+      LAccount := FNode.GetMempoolAccount(LIndexKey);
+      while (Random(100)>0) or (LAccount.balance>0) or (Length(LAccount.name)>0) do begin
+        if Not LAccOrd.Next(LIndexKey) then Exit;
+        LAccount := FNode.GetMempoolAccount(LIndexKey);
+      end;
+    end;
+    //
+  end;
+
+  TLog.NewLog(ltdebug,ClassName,Format('Sending to %s Account %s PublicKey %s',
+    [ASenderConnection.Client.RemoteHost,
+     TAccountComp.AccountNumberToAccountTxtNumber(LAccount.account),
+     TAccountComp.AccountPublicKeyExport(LSenderPublicKey)]));
+
+  LOpRecoverFounds := TOpRecoverFounds.Create(FNode.Bank.SafeBox.CurrentProtocol,LAccount.account,LAccount.n_operation+1,0,LSenderPublicKey);
   try
-    FNode.AddOperation(Nil,LOpChangeKey,LErrors);
+    if FNode.AddOperation(Nil,LOpRecoverFounds,LErrors) then begin
+      Lword := 1;
+      AResponseData.Write(Lword,2);
+      LRaw := LOpRecoverFounds.OperationHashValid(LOpRecoverFounds,0);
+      TStreamOp.WriteAnsiString(AResponseData,LRaw);
+    end else begin
+      Lword := 0;
+      AResponseData.Write(Lword,2);
+      TLog.NewLog(ltdebug,ClassName,Format('Error %s sending to %s Account %s PublicKey %s',
+        [LErrors, ASenderConnection.Client.RemoteHost,
+         TAccountComp.AccountNumberToAccountTxtNumber(LAccount.account),
+         TAccountComp.AccountPublicKeyExport(LSenderPublicKey)]));
+    end;
   finally
-    LOpChangeKey.Free;
+    LOpRecoverFounds.Free;
   end;
-  // Response
-  TStreamOp.WriteAccountKey(AResponseData,LSenderPublicKey);
-  LWord := 1;
-  AResponseData.Write(LWord,SizeOf(LWord));
-  AResponseData.Write(LAccount.account,SizeOf(LAccount.account));
 end;
 
+{$IFDEF TESTNET}
 procedure TPCTNetDataExtraMessages.DoGiveMeMoney(ANetData: TNetData;
   ASenderConnection: TNetConnection; const AHeaderData: TNetHeaderData;
   AReceivedData, AResponseData: TStream);
@@ -207,6 +244,7 @@ class function TPCTNetDataExtraMessages.InitNetDataExtraMessages(ANode: TNode;
   ANetData: TNetData; AWalletKeys: TWalletKeysExt): TPCTNetDataExtraMessages;
 begin
   if not Assigned(_PCTNetDataExtraMessages) then begin
+    TLog.NewLog(ltinfo,ClassName,'InitNetDataExtraMessages');
     _PCTNetDataExtraMessages := TPCTNetDataExtraMessages.Create(ANode,ANetData,AWalletKeys);
   end;
   Result := _PCTNetDataExtraMessages;
@@ -215,12 +253,12 @@ end;
 procedure TPCTNetDataExtraMessages.OnTNetDataProcessReservedAreaMessage(ANetData : TNetData; ASenderConnection : TNetConnection; const AHeaderData : TNetHeaderData; AReceivedData : TStream; AResponseData : TStream);
 begin
   TLog.NewLog(ltdebug,ClassName,Format('Received extra message from %s Operation:%d',[ASenderConnection.ClientRemoteAddr,AHeaderData.operation]));
-  {$IFDEF TESTNET}
   case AHeaderData.operation of
     CT_NetProtocol_Extra_NetOp_GIVE_ME_AN_ACCOUNT : DoGiveMeAnAccount(ANetData,ASenderConnection,AHeaderData,AReceivedData,AResponseData);
+    {$IFDEF TESTNET}
     CT_NetProtocol_Extra_NetOp_GIVE_ME_MONEY : DoGiveMeMoney(ANetData,ASenderConnection,AHeaderData,AReceivedData,AResponseData);
+    {$ENDIF}
   end;
-  {$ENDIF}
 end;
 
 function TPCTNetDataExtraMessages.RandomGetWalletKeysAccount(

+ 76 - 0
src/core/UPCTemporalAbstractMem.pas

@@ -0,0 +1,76 @@
+unit UPCTemporalAbstractMem;
+
+{ Copyright (c) 2016-2021 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+interface
+
+{$I ./../config.inc}
+
+uses
+  Classes, {$IFnDEF FPC}Windows,{$ENDIF} SysUtils,
+  UPCTemporalFileStream,
+  UAbstractMem, UFileMem;
+
+Type
+  { TPCTemporalAbstractMem }
+
+  TPCTemporalAbstractMem = Class({$IFDEF USE_ABSTRACTMEM}TFileMem{$ELSE}TMem{$ENDIF})
+  private
+    {$IFDEF USE_ABSTRACTMEM}
+    FTemporalFileName : String;
+    {$ENDIF}
+  protected
+  public
+    Constructor Create; reintroduce;
+    Destructor Destroy; override;
+  End;
+
+implementation
+
+Uses {$IFDEF HIGHLOG}ULog, {$ENDIF} UNode;
+
+{ TPCTemporalFileStream }
+
+constructor TPCTemporalAbstractMem.Create;
+begin
+  {$IFDEF USE_ABSTRACTMEM}
+  FTemporalFileName := TPCTemporalFileStream.GetTemporalFileName('ABSTRACTMEM');
+  {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,ClassName,Format('Creating a new Temporal AbstractMem file: %s',[FTemporalFileName]));{$ENDIF}
+  inherited Create(FTemporalFileName,False);
+  {$ELSE}
+  inherited Create(0,False);
+  {$ENDIF}
+end;
+
+destructor TPCTemporalAbstractMem.Destroy;
+{$IFDEF HIGHLOG}var LSize : Integer;{$ENDIF}
+begin
+  {$IFDEF HIGHLOG}LSize := Size;{$ENDIF}
+  inherited Destroy;
+  {$IFDEF USE_ABSTRACTMEM}
+  if FTemporalFileName<>'' then begin
+    {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,ClassName,Format('Deleting a Temporal AbstractMem file (%d bytes): %s',[LSize, FTemporalFileName]));{$ENDIF}
+    DeleteFile(FTemporalFileName);
+  end;
+  {$ENDIF}
+end;
+
+end.

+ 5 - 7
src/core/UPoolMinerThreads.pas

@@ -36,10 +36,11 @@ type
     WorkingMillisecondsTotal : Cardinal;
     WinsCount : Integer;
     Invalids : Integer;
+    InternalComputingRounds : Integer;
   End;
 
 Const
-  CT_TMinerStats_NULL : TMinerStats = (Miners:0;RoundsCount:0;WorkingMillisecondsHashing:0;WorkingMillisecondsTotal:0;WinsCount:0;Invalids:0);
+  CT_TMinerStats_NULL : TMinerStats = (Miners:0;RoundsCount:0;WorkingMillisecondsHashing:0;WorkingMillisecondsTotal:0;WinsCount:0;Invalids:0;InternalComputingRounds:0);
 
 Type
 
@@ -626,11 +627,6 @@ begin
 end;
 
 procedure TCustomMinerDeviceThread.UpdateDeviceStats(Stats: TMinerStats);
-Type TTimeMinerStats = Record
-       tc : Cardinal;
-       stats : TMinerStats;
-     end;
-  PTimeMinerStats = ^TTimeMinerStats;
 Var l : TList<Pointer>;
   i : Integer;
   P : PTimeMinerStats;
@@ -657,6 +653,7 @@ begin
         if ((stats.Miners>foundMaxMiners)) then foundMaxMiners := stats.Miners;
       end;
     end;
+    FPartialDeviceStats.InternalComputingRounds:=l.count;
     If l.count>0 then begin
       P := PTimeMinerStats(l[l.count-1]);
       FPartialDeviceStats.WorkingMillisecondsHashing:=P^.tc - PTimeMinerStats(l[0]).tc + P^.stats.WorkingMillisecondsHashing;
@@ -863,7 +860,7 @@ begin
             if FCurrentMinerValuesForWork.version < CT_PROTOCOL_5 then
               roundsToDo := 20
             else
-              roundsToDo := 200+Random(200);
+              roundsToDo := 100+Random(100);
           end else begin
             roundsToDo := 10000;
           end;
@@ -982,6 +979,7 @@ begin
               finalHashingTC:=TPlatform.GetTickCount;
             end;
             AuxStats.Miners:=FCPUDeviceThread.FCPUs;
+            AuxStats.InternalComputingRounds:=roundsToDo;
             AuxStats.RoundsCount:=LRoundsPerformed;
             AuxStats.WorkingMillisecondsTotal:=TPlatform.GetTickCount - baseRealTC;
             AuxStats.WorkingMillisecondsHashing:= finalHashingTC - baseHashingTC;

File diff suppressed because it is too large
+ 431 - 188
src/core/URPC.pas


+ 107 - 26
src/core/USettings.pas

@@ -39,6 +39,7 @@ const
   CT_PARAM_SaveDebugLogs = 'SaveDebugLogs';
   CT_PARAM_ShowLogs = 'ShowLogs';
   CT_PARAM_MinerName = 'MinerName';
+  CT_PARAM_MaxPayToKeyPurchasePrice = 'MaxPayToKeyPurchasePrice';
   CT_PARAM_RunCount = 'RunCount';
   CT_PARAM_FirstTime = 'FirstTime';
   CT_PARAM_ShowModalMessages = 'ShowModalMessages';
@@ -61,6 +62,10 @@ type
 
   TMinerPrivateKeyType = (mpk_NewEachTime, mpk_Random, mpk_Selected);
 
+  { TShowHashRateAs }
+
+  TShowHashRateAs = (hr_Unit, hr_Kilo, hr_Mega, hr_Giga, hr_Tera, hr_Peta, hr_Exa);
+
   { TSettings }
 
   TSettings = class
@@ -72,19 +77,15 @@ type
       class function GetMinFutureBlocksToDownloadNewSafebox: Integer; static;
       class procedure SetAllowDownloadNewCheckpointIfOlderThan(ABool: Boolean); static;
       class procedure SetInternetServerPort(AInt:Integer); static;
-      class function GetRpcPortEnabled : boolean; static;
+      class function GetJsonRpcPortEnabled : boolean; static;
       class procedure SetMinFutureBlocksToDownloadNewSafebox(AInt: Integer); static;
-      class procedure SetRpcPortEnabled(ABool: boolean); static;
+      class procedure SetJsonRpcPortEnabled(ABool: boolean); static;
       class function GetDefaultFee : Int64; static;
       class procedure SetDefaultFee(AInt64: Int64); static;
       class function GetMinerPrivateKeyType : TMinerPrivateKeyType; static;
       class procedure SetMinerPrivateKeyType(AType: TMinerPrivateKeyType); static;
-      class function GetMinerSelectedPrivateKey : TRawBytes; static;
-      class procedure SetMinerSelectedPrivateKey(AKey:TRawBytes); static;
-      class function GetMinerServerRpcActive : boolean; static;
-      class procedure SetMinerServerRpcActive(ABool: Boolean); static;
-      class function GetMinerServerRpcPort : Integer; static;
-      class procedure SetMinerServerRpcPort(APort: Integer); static;
+      class function GetMinerSelectedPublicKey : TRawBytes; static;
+      class procedure SetMinerSelectedPublicKey(AKey:TRawBytes); static;
       class function GetSaveLogFiles : boolean; static;
       class procedure SetSaveLogFiles(ABool: boolean); static;
       class function GetShowLogs : boolean; static;
@@ -95,14 +96,27 @@ type
       class procedure SetMinerName(AName: string); static;
       class function GetRunCount : Integer; static;
       class procedure SetRunCount(AInt: Integer); static;
+      class function GetFirstTime : Boolean; static;
+      class procedure SetFirstTime(ABool: Boolean); static;
+      class function GetMaxPayToKeyPurchasePrice : UInt64; static;
+      class procedure SetMaxPayToKeyPurchasePrice(AVal: UInt64); static;
       class function GetShowModalMessages : boolean; static;
       class procedure SetShowModalMessages(ABool: boolean); static;
-      class function GetRpcAllowedIPs : string; static;
-      class procedure SetRpcAllowedIPs(AString: string); static;
+      class function GetJsonRpcAllowedIPs : string; static;
+      class procedure SetJsonRpcAllowedIPs(AString: string); static;
+      class function GetJsonRpcMinerServerActive : boolean; static;
+      class procedure SetJsonRpcMinerServerActive(ABool: Boolean); static;
+      class function GetMinerServerRpcPort : Integer; static;
+      class procedure SetMinerServerRpcPort(APort: Integer); static;
+      class function GetHashRateAvgBlocksCount : Integer; static;
+      class procedure SetHashRateAvgBlocksCount(AInt: Integer); static;
+      class function GetShowHashRateAs : TShowHashRateAs; static;
+      class procedure SetShowHashRateAs(AVal: TShowHashRateAs); static;
       class function GetPeerCache : string; static;
       class procedure SetPeerCache(AString: string); static;
       class function GetTryConnectOnlyWithThisFixedServers : string; static;
       class procedure SetTryConnectOnlyWithThisFixedServers(AString: string); static;
+      class procedure CheckNotLoaded;
       class procedure CheckLoaded;
       class procedure NotifyOnChanged;
     public
@@ -110,18 +124,22 @@ type
       class procedure Save;
       class property OnChanged : TNotifyManyEvent read FOnChanged;
       class property InternetServerPort : Integer read GetInternetServerPort write SetInternetServerPort;
-      class property RpcPortEnabled : boolean read GetRpcPortEnabled write SetRpcPortEnabled;
-      class property RpcAllowedIPs : string read GetRpcAllowedIPs write SetRpcAllowedIPs;
+      class property JsonRpcPortEnabled : boolean read GetJsonRpcPortEnabled write SetJsonRpcPortEnabled;
+      class property JsonRpcAllowedIPs : string read GetJsonRpcAllowedIPs write SetJsonRpcAllowedIPs;
+      class property JsonRpcMinerServerActive : boolean read GetJsonRpcMinerServerActive write SetJsonRpcMinerServerActive;
+      class property JsonRpcMinerServerPort : Integer read GetMinerServerRpcPort write SetMinerServerRpcPort;
+      class property HashRateAvgBlocksCount : Integer read GetHashRateAvgBlocksCount write SetHashRateAvgBlocksCount;
+      class property ShowHashRateAs : TShowHashRateAs read GetShowHashRateAs write SetShowHashRateAs;
       class property DefaultFee : Int64 read GetDefaultFee write SetDefaultFee;
       class property MinerPrivateKeyType : TMinerPrivateKeyType read GetMinerPrivateKeyType write SetMinerPrivateKeyType;
-      class property MinerSelectedPrivateKey : TRawBytes read GetMinerSelectedPrivateKey write SetMinerSelectedPrivateKey;
-      class property MinerServerRpcActive : boolean read GetMinerServerRpcActive write SetMinerServerRpcActive;
-      class property MinerServerRpcPort : Integer read GetMinerServerRpcPort write SetMinerServerRpcPort;
+      class property MinerSelectedPublicKey : TRawBytes read GetMinerSelectedPublicKey write SetMinerSelectedPublicKey;
       class property SaveLogFiles : boolean read GetSaveLogFiles write SetSaveLogFiles;
       class property ShowLogs : boolean read GetShowLogs write SetShowLogs;
       class property SaveDebugLogs : boolean read GetSaveDebugLogs write SetSaveDebugLogs;
       class property MinerName : string read GetMinerName write SetMinerName;
       class property RunCount : Integer read GetRunCount write SetRunCount;
+      class property FirstTime : Boolean read GetFirstTime write SetFirstTime;
+      class property MaxPayToKeyPurchasePrice : UInt64 read GetMaxPayToKeyPurchasePrice write SetMaxPayToKeyPurchasePrice;
       class property ShowModalMessages : boolean read GetShowModalMessages write SetShowModalMessages;
       class property PeerCache : string read GetPeerCache write SetPeerCache;
       class property TryConnectOnlyWithThisFixedServers : string read GetTryConnectOnlyWithThisFixedServers write SetTryConnectOnlyWithThisFixedServers;
@@ -140,13 +158,22 @@ uses
 
 class procedure TSettings.Load;
 begin
+  CheckNotLoaded;
   FAppParams := TAppParams.Create(nil);
   FAppParams.FileName := TNode.GetPascalCoinDataFolder+PathDelim+'AppParams.prm';
+
+  If FAppParams.FindParam(CT_PARAM_MinerName)=Nil then begin
+    // New configuration... assigning a new random value
+    FAppParams.ParamByName[CT_PARAM_MinerName].SetAsString('New Node '+DateTimeToStr(Now)+' - '+ CT_ClientAppVersion);
+  end;
+
 end;
 
 class procedure TSettings.Save;
 begin
   //TODO Update FAppParams to optionally save on set value, and make FApp.Save public and verify all AppParams updates in client code
+  CheckLoaded;
+  OnChanged.Invoke(Nil);
 end;
 
 class function TSettings.GetInternetServerPort : Integer;
@@ -179,36 +206,60 @@ begin
   FAppParams.ParamByName[CT_PARAM_InternetServerPort].SetAsInteger(AInt);
 end;
 
-class function TSettings.GetRpcPortEnabled : boolean;
+class procedure TSettings.SetMinFutureBlocksToDownloadNewSafebox(AInt: Integer);
 begin
   CheckLoaded;
-  Result := FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].GetAsBoolean(false);
+  FAppParams.ParamByName[CT_PARAM_MinFutureBlocksToDownloadNewSafebox].SetAsInteger(AInt);
 end;
 
-class procedure TSettings.SetMinFutureBlocksToDownloadNewSafebox(AInt: Integer);
+class function TSettings.GetJsonRpcPortEnabled : boolean;
 begin
   CheckLoaded;
-  FAppParams.ParamByName[CT_PARAM_MinFutureBlocksToDownloadNewSafebox].SetAsInteger(AInt);
+  Result := FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].GetAsBoolean(false);
 end;
 
-class procedure TSettings.SetRpcPortEnabled(ABool: boolean);
+class procedure TSettings.SetJsonRpcPortEnabled(ABool: boolean);
 begin
   CheckLoaded;
   FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].SetAsBoolean(ABool);
 end;
 
-class function TSettings.GetRpcAllowedIPs : string;
+class function TSettings.GetJsonRpcAllowedIPs : string;
 begin
   CheckLoaded;
   Result := FAppParams.ParamByName[CT_PARAM_JSONRPCAllowedIPs].GetAsString('127.0.0.1;');
 end;
 
-class procedure TSettings.SetRpcAllowedIPs(AString: string);
+class procedure TSettings.SetJsonRpcAllowedIPs(AString: string);
 begin
   CheckLoaded;
   FAppParams.ParamByName[CT_PARAM_JSONRPCAllowedIPs].SetAsString(AString);
 end;
 
+class function TSettings.GetHashRateAvgBlocksCount : Integer;
+begin
+  CheckLoaded;
+  Result := FAppParams.ParamByName[CT_PARAM_HashRateAvgBlocksCount].GetAsInteger(50)
+end;
+
+class procedure TSettings.SetHashRateAvgBlocksCount(AInt: Integer);
+begin
+  CheckLoaded;
+  FAppParams.ParamByName[CT_PARAM_HashRateAvgBlocksCount].SetAsInteger(AInt);
+end;
+
+class function TSettings.GetShowHashRateAs : TShowHashRateAs;
+begin
+  CheckLoaded;
+  Result := TShowHashRateAs(FAppParams.ParamByName[CT_PARAM_ShowHashRateAs].GetAsInteger(Integer({$IFDEF TESTNET}hr_Mega{$ELSE}hr_Tera{$ENDIF})));
+end;
+
+class procedure TSettings.SetShowHashRateAs(AVal: TShowHashRateAs);
+begin
+  CheckLoaded;
+  FAppParams.ParamByName[CT_PARAM_ShowHashRateAs].SetAsInteger(Integer(AVal));
+end;
+
 class function TSettings.GetDefaultFee : Int64;
 begin
   CheckLoaded;
@@ -221,13 +272,13 @@ begin
   FAppParams.ParamByName[CT_PARAM_DefaultFee].SetAsInt64(AInt64);
 end;
 
-class function TSettings.GetMinerServerRpcActive : boolean;
+class function TSettings.GetJsonRpcMinerServerActive : boolean;
 begin
   CheckLoaded;
   Result := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].GetAsBoolean(true);
 end;
 
-class procedure TSettings.SetMinerServerRpcActive(ABool: Boolean);
+class procedure TSettings.SetJsonRpcMinerServerActive(ABool: Boolean);
 begin
   CheckLoaded;
   FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].SetAsBoolean(ABool);
@@ -257,13 +308,13 @@ begin
   FAppParams.ParamByName[CT_PARAM_MinerPrivateKeyType].SetAsInteger(Integer(AType));
 end;
 
-class function TSettings.GetMinerSelectedPrivateKey : TRawBytes;
+class function TSettings.GetMinerSelectedPublicKey : TRawBytes;
 begin
   CheckLoaded;
   Result := FAppParams.ParamByName[CT_PARAM_MinerPrivateKeySelectedPublicKey].GetAsTBytes(Nil);
 end;
 
-class procedure TSettings.SetMinerSelectedPrivateKey(AKey:TRawBytes);
+class procedure TSettings.SetMinerSelectedPublicKey(AKey:TRawBytes);
 begin
   CheckLoaded;
   FAppParams.ParamByName[CT_PARAM_MinerPrivateKeySelectedPublicKey].SetAsTBytes(AKey);
@@ -329,6 +380,30 @@ begin
   FAppParams.ParamByName[CT_PARAM_RunCount].SetAsInteger(AInt)
 end;
 
+class function TSettings.GetFirsttime : Boolean;
+begin
+  CheckLoaded;
+  Result := FAppParams.ParamByName[CT_PARAM_FirstTime].GetAsBoolean(true);
+end;
+
+class procedure TSettings.SetFirstTime(ABool: Boolean);
+begin
+  CheckLoaded;
+  FAppParams.ParamByName[CT_PARAM_FirstTime].SetAsBoolean(ABool);
+end;
+
+class function TSettings.GetMaxPayToKeyPurchasePrice : UInt64;
+begin
+  CheckLoaded;
+  Result := FAppParams.ParamByName[CT_PARAM_MaxPayToKeyPurchasePrice].GetAsUInt64(CT_DEFAULT_PAY_TO_KEY_MAX_MOLINAS);
+end;
+
+class procedure TSettings.SetMaxPayToKeyPurchasePrice(AVal: UInt64);
+begin
+  CheckLoaded;
+  FAppParams.ParamByName[CT_PARAM_MaxPayToKeyPurchasePrice].SetAsUInt64(AVal);
+end;
+
 class function TSettings.GetShowModalMessages : boolean;
 begin
   CheckLoaded;
@@ -365,6 +440,12 @@ begin
   FAppParams.ParamByName[CT_PARAM_TryToConnectOnlyWithThisFixedServers].SetAsString(Trim(AString));
 end;
 
+class procedure TSettings.CheckNotLoaded;
+begin
+  if Assigned(FAppParams) then
+    raise Exception.Create('Application settings have already been loaded');
+end;
+
 class procedure TSettings.CheckLoaded;
 begin
   if not Assigned(FAppParams) then

+ 6 - 1
src/core/UTCPIP.pas

@@ -333,7 +333,12 @@ end;
 
 destructor TNetTcpIpClient.Destroy;
 begin
-  Disconnect;
+  FLock.Acquire;
+  try
+    Disconnect;
+  finally
+    FLock.Release;
+  end;
   {$IFDEF Synapse}  // Memory leak on 1.5.0
   FreeAndNil(FSendBufferLock);
   {$ENDIF}

+ 1 - 1
src/core/UTxMultiOperation.pas

@@ -23,7 +23,7 @@ unit UTxMultiOperation;
 interface
 
 uses
-  Classes, SysUtils, UCrypto, UBlockChain, UAccounts, UBaseTypes,
+  Classes, SysUtils, UCrypto, UBlockChain, UAccounts, UBaseTypes, UEPasa,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   UPCDataTypes;
 

+ 2 - 2
src/core/UWallet.pas

@@ -592,7 +592,7 @@ begin
   Result := CT_TECDSA_Public_Nul;
   case TSettings.MinerPrivateKeyType of
     mpk_NewEachTime: PublicK := CT_TECDSA_Public_Nul;
-    mpk_Selected: PublicK := TAccountComp.RawString2Accountkey(TSettings.MinerSelectedPrivateKey);
+    mpk_Selected: PublicK := TAccountComp.RawString2Accountkey(TSettings.MinerSelectedPublicKey);
     mpk_Random: begin
       PublicK := CT_TECDSA_Public_Nul;
       if FKeys.Count>0 then PublicK := FKeys.Key[Random(FKeys.Count)].AccountKey;
@@ -612,7 +612,7 @@ begin
       // Set to Settings if not mpk_NewEachTime
       if (TSettings.MinerPrivateKeyType<>mpk_NewEachTime) then begin
         TSettings.MinerPrivateKeyType:=mpk_Selected;
-        TSettings.MinerSelectedPrivateKey := TAccountComp.AccountKey2RawString(PublicK);
+        TSettings.MinerSelectedPublicKey := TAccountComp.AccountKey2RawString(PublicK);
       end;
     finally
       PK.Free;

+ 3 - 5
src/core/upcdaemon.pas

@@ -48,6 +48,7 @@ Const
   CT_INI_IDENT_MINPENDINGBLOCKSTODOWNLOADCHECKPOINT = 'MINPENDINGBLOCKSTODOWNLOADCHECKPOINT';
   CT_INI_IDENT_PEERCACHE = 'PEERCACHE';
   CT_INI_IDENT_DATA_FOLDER = 'DATAFOLDER';
+  CT_INI_IDENT_NODE_MAX_PAYTOKEY_MOLINAS = 'MAX_PAYTOKEY_MOLINAS';
   {$IFDEF USE_ABSTRACTMEM}
   CT_INI_IDENT_ABSTRACTMEM_MAX_CACHE_MB = 'ABSTRACTMEM_MAX_CACHE_MB';
   CT_INI_IDENT_ABSTRACTMEM_USE_CACHE_ON_LISTS = 'ABSTRACTMEM_USE_CACHE_ON_LISTS';
@@ -105,9 +106,7 @@ Type
 
 implementation
 
-{$IFDEF TESTNET}
 uses UPCTNetDataExtraMessages;
-{$ENDIF}
 
 Var _FLog : TLog;
 
@@ -276,9 +275,6 @@ begin
       FNode.Bank.SafeBox.PCAbstractMem.MaxAccountsCache := LCacheMaxAccounts;
       FNode.Bank.SafeBox.PCAbstractMem.MaxAccountKeysCache := LCacheMaxPubKeys;
       {$ENDIF}
-      {$IFDEF TESTNET}
-      TPCTNetDataExtraMessages.InitNetDataExtraMessages(FNode,TNetData.NetData,FWalletKeys);
-      {$ENDIF}
       // RPC Server
       InitRPCServer;
       Try
@@ -295,12 +291,14 @@ begin
         FWalletKeys.SafeBox := FNode.Node.Bank.SafeBox;
         FNode.Node.NetServer.Port:=FIniFile.ReadInteger(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_NODE_PORT,CT_NetServer_Port);
         FNode.Node.NetServer.MaxConnections:=FIniFile.ReadInteger(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_NODE_MAX_CONNECTIONS,CT_MaxClientsConnected);
+        FNode.Node.MaxPayToKeyPurchasePrice:=FIniFile.ReadInt64(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_NODE_MAX_PAYTOKEY_MOLINAS,CT_DEFAULT_PAY_TO_KEY_MAX_MOLINAS);
         FNode.Node.AutoDiscoverNodes(CT_Discover_IPs);
         FNode.Node.NetServer.Active := true;
 
         // RPC Miner Server
         InitRPCMinerServer;
         Try
+          TPCTNetDataExtraMessages.InitNetDataExtraMessages(FNode,TNetData.NetData,FWalletKeys);
           Repeat
             Sleep(100);
           Until (Terminated) or (Application.Terminated);

+ 3914 - 0
src/gui-classic/UFRMAskForAccount.dfm

@@ -0,0 +1,3914 @@
+object FRMAskForAccount: TFRMAskForAccount
+  Left = 0
+  Top = 0
+  ActiveControl = ebMessageToSend
+  BorderIcons = [biSystemMenu]
+  BorderStyle = bsDialog
+  Caption = 'Ask for Account (PASA)'
+  ClientHeight = 388
+  ClientWidth = 615
+  Color = clBtnFace
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
+  Font.Style = []
+  OldCreateOrder = True
+  Position = poOwnerFormCenter
+  OnCreate = FormCreate
+  DesignSize = (
+    615
+    388)
+  PixelsPerInch = 96
+  TextHeight = 13
+  object lblDoYouWantToSendAMessage: TLabel
+    Left = 17
+    Top = 288
+    Width = 446
+    Height = 13
+    Caption = 
+      'Do you want to send a message that will be included in the block' +
+      'chain? (visible to everybody)'
+    Color = clBtnFace
+    ParentColor = False
+    Transparent = False
+  end
+  object imgAskForAccount: TImage
+    Left = 8
+    Top = 64
+    Width = 200
+    Height = 200
+    Picture.Data = {
+      07544269746D6170F6D40100424DF6D40100000000003600000028000000C800
+      0000C80000000100180000000000C0D40100120B0000120B0000000000000000
+      0000D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D1D1D1CFCFCFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B2AA9D
+      AB9470B3B0AAD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CCCCCC9078556C471D7D5D34A493
+      79BFBFBFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6AEA99F7151293121203A27205F3F1C8D7047ADA494CD
+      CDCDD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D0
+      D0D097826251371E2B1D202C1E202D1F20472F1F704E2599825FB6B2ABD6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B1AEAA7D5C313A2720
+      2D1E202F20202E20202C1D2032222054381E805F34A49379BFBFBFD6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D5D5D59E8D73593B1D2D1E202F20202F20202F20
+      202F20202E1F202C1E203A272060411D8D7047ACA392CECECED6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6BAB9B882653C3C28202D1E202F20202F20202F20202F20202F20202F
+      20202D1F202E1F20462F1F714F2699815EB6B1AAD6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6A1947E61
+      421E2D1E202E20202F20202F20202F20202F20202F20202F20202F20202E1F20
+      2C1E2033222053371E805F33A39379BFBFBFD6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6C3C3C3896E46462F1F2C1E202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202E1F202C1E
+      203B291F5F3F1C8D7047ADA393CECECED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6A9A0916A49233020202E20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202D1E202D1F2046
+      301F6F4E259A825EB6B3ACD3D3D3D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CACA
+      CA9078544A311F2C1D202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202E20202C1E2032212054381E
+      98733CBBB3A4D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6ACA69C7553283423202D
+      1F202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202C1E20322220795528B1ABA0D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D3D3D396816354381E2C1E202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202C1E204B321F927B59CCCCCCD6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D0D0D0C6C6C6B6
+      B6B5B1AEA8B3ACA0ADA596A095859D92819D92819D92819D9281A79D8DB3AA9C
+      B1ACA2B2B1ADBFBFBFCDCDCDD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6B5B2AD7A5B333725202D1F202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202E
+      1F203222206C4C25AAA295D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D3D3D3C4C4C4B4B0AAA89F8F9F8E748E785B876E4B7D5F3976582F684B29
+      5F42245C3E1E5C3E1E5B3E1E5B3E1E5C3E1E5E402064472770512C7B5B328266
+      418C755698876DA79C8AB1ADA5C2C2C2D3D3D3D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D69D8C73583B
+      1D2C1E202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202C1E204A311E8E724B
+      C5C5C5D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CDCDCDB6B4B0A39A8A988568836947
+      74563163442255381D4B321F3D29203927203725203021202D1F202C1E202C1E
+      202C1E202C1E202C1E202D1F202E1F203423203826203A2720442E1F51361E5B
+      3E2073542E8469459A8669ABA292BBBAB9D3D3D3D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6BEBDBC87683E402B1F2C1E202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202E20202E1F2063441FA39885D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      C9C9C9B3B0AAA69882947B567B5B346142204C331E402A203323202F20202C1E
+      202C1E202D1E202D1F202D1F202E1F202F20202F20202F20202F20202F20202F
+      20202F20202E20202E1F202D1F202D1F202C1E202B1E202E1F20322220402B20
+      52371E6B4B2485673C9B8667B0A89AC4C4C4D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6A699846444202E1F202E20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202D1E203F2A1F866842BEBDBDD6D6D6D6D6D6D3D3D3C9C9C9D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CACACAB5B0A8A1927C8A725074552F6142
+      1E4B321F3826202E1F202C1E202D1E202E1F202E20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202E1F202D1E202C1E202F20203D29
+      2053371E6F4F278E724AA79982BEBDBBD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6C3C3C3
+      896E48432D1F2C1E202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202D1F205E
+      401FA3947CD6D6D6D6D6D6D6D6D6B5B0A8AF8F5DA69A87C9C9C9D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6C9C9
+      C9B4B0A89F927C8B725071532E57391D442D1F3423202D1F202C1E202D1F202E
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202E1F202C1E202B1E2032222046
+      2E1F5F411E8B6C40A9997EBDBCBAD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6A9A0916B4A233021202E1F
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202D1E203E2A1F806237B8B7B4D6D6D6
+      D6D6D6D1D1D19C8666734C1C6C4B22967D57B4AEA4D2D2D2D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6C9C9C9B4B0A8A1927C8A714F71522F573A1D43
+      2D1F3423202D1E202C1E202D1F202E20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202E1F202C1E202D1F203F2B1F
+      61411D8B6C40A9987EBEBDBCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6CECECE967E594E341E2C1E202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202C1E205A3B1D9B886BD3D3D3D6D6D6D6D6D6B3B0AC805F
+      3239262030212052371E7A5A2F9D8767B6B1A7D2D2D2D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6C2C2C2AF
+      A99F9D8E7689714F71532F56391D442D1F3423202C1E202C1E202E1F202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202E20202C1E202D1F203F2A1F6141
+      1D8A6C42A79B86C6C6C6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6AFAAA074542C3322202D1F202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202D1F
+      2035242077572FB1AEA7D6D6D6D6D6D6D6D6D69F8F765B3D1D2C1E202D1F202B
+      1E2036242052371E7A5A31988262ADA699C5C5C5D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6CECECEBAB8B6A8A09298866C8A6D46715129573A1D432D1F
+      3423202D1E202C1E202E1F202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202E20202D1E202D1E20412C1F6B4A2196
+      7A52B1A99BCECECED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D2D2D296826350351E
+      2C1E202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202C1E2052371E9A8462D1
+      D1D1D6D6D6D6D6D6BCBBBA85673F3D29202D1E202F20202F20202D1F202C1E20
+      36252051361E6D4E27896F4BA3947CB5B0A8C6C6C6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CCCCCCBABAB9AFA89C9E8F7A
+      9079597E613965462351361E422C1F3322202C1E202C1E202E1F202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202E20202C1E203121204C331F755329998260
+      B6B3ABD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B6B4AF7D5D333927202D1F202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202D1F20342320755329AFA99FD6D6D6D6D6D6D6D6D6
+      A3978263431F2E1F202F20202F20202F20202F20202F20202D1F202C1E203121
+      20452E1F5E3F1D75552E866D4B99886DA79E8FB5B1AABFBFBFCBCBCBD4D4D4D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D5D5D5CFCFCFC3C3C3
+      BCBBB9AFABA3A79D8C9D8C718E77547D603B6A4C28573A1D48301F3B27202F20
+      202C1E202C1E202E1F202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202E1F202B1D20342320583A1E836234A39176BAB9
+      B9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6A291775B3E1E2D1E202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202C1E2049311F907652CACACAD6D6D6D6D6D6C4C4C48C704949311F2C1E
+      202F20202F20202F20202F20202F20202F20202F20202E1F202C1E202D1E2034
+      2320412B1F51351E61422271532F7F633F8A72519783669B8B719E8F799E917E
+      9E93839E94849E917E9E8F799D8C7398866A8E7858866B477B5E3A6D4E2B6344
+      2253371E49311F3A27203121202C1E202C1E202D1F202E1F202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202D1F202C1E203A27205D3E1D8B6C3FAC9E8AC8C8C8D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6BBBBB982
+      653E3D29202D1E202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202E20202F20206646
+      23A69D8ED6D6D6D6D6D6D6D6D6ABA2936C4B253121202E1F202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202E20202D1F202C1E202C1E20
+      2F1F203323203D2920442E1F47301F56391D5D3E1D5D3E1E5D3E1E5D3F1F5C3F
+      1E5E3E1D5A3C1D4A311E452E1F432D1F3926203121202F20202C1E202C1E202D
+      1F202E1F202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202D1E202D1F20452E1F6A4A22917852B1AA9FD3D3D3D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6A1958260401F2D1F202E2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202C1E20432E1F8D7149C4C4C4D6D6D6D6
+      D6D6CBCBCB9178564A311F2C1E202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202E20202E1F202D1F
+      202C1E202C1E202C1E202C1E202C1E202C1E202C1E202C1E202C1E202C1E202C
+      1E202C1E202C1E202D1F202E1F202E20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202E20202C1E202F20204C331F78572C9D8869B6B4AFD6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6C6C6C68D724B472F1F2C1E202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202E20202F202064431FA39783D6D6D6D6D6D6D6D6D6ACA69D6E4F29
+      3222202E1F202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202E20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202E1F202C1E20362420593B1D856539A79881C3C3C3D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6ABA2946C4C253121202E1F202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202C1E20
+      402B1F86663CB9B8B7D6D6D6D6D6D6D3D3D398846554381E2C1E202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202D
+      1F202D1F203F2A1F67461F92754CAFA79BD3D3D3D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CCCCCC8E775649311F2C
+      1E202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202C1E20583A1D9E8D73D5D5
+      D5D6D6D6D6D6D6B6B3AE7E5D343926202D1F202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202C1E20
+      3021204C331F77552BA28D6DBBB9B6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6AEA9A07252293322202D1F202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202D1F20392620805F35B7B4AFD6D6D6D6D6D6D5D5D59B
+      8B7256391D2C1E202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202E1F202B1D203624
+      205B3D1D84663CA99D8ACBCBCBD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D3D3D39B876855391E2C1E202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202C1E2056391D9D896AD3D3D3D6D6D6D6D6D6BBBAB980633C3C28202C1E20
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202D1F202C1E20402B206C
+      4A239A825DB8B4AED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B5B3AE7C5D
+      343826202D1F202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202D1F2034232075542B
+      AEAAA2D6D6D6D6D6D6D6D6D6A6998463431F2E1F202E20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202C1E2031212054381E876638
+      AA9C84C9C9C9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D5D5D59C8B7356391D2C1E202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202C1E204B321F907959CDCDCDD6D6D6D6D6
+      D6C4C4C48D714B442D1F2C1E202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202E20202E20202E20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202E1F202C1E203F2A1F6C4B219D825AB7B3
+      ABD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6BDBDBC84673F402B1F2C1E202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202E1F203121206E4D26ABA496D6D6D6D6D6D6D6D6D6A69D8F6646232F
+      20202E1F202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202E20202E1F202E1F202D1E202C1E202C
+      1E202C1E202C1E202D1E202D1E202D1E202C1E202C1E202C1E202E1F202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202C1E2031212053371E886638A99C86CBCBCBD6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      A79B886445212F20202E20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202C1E2049
+      311F8F744FC9C9C9D6D6D6D6D6D6CCCCCC9178554C321F2C1E202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202E20202D1F202D1F202C
+      1E202B1D202C1E202E1F203121203423203F2A2047301F47301F47301E54381D
+      5F401D60401D5F401D56391D49311F442E1F3625202D1F202D1E202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202E1F202C1E203E2A1F6E4C229A825EB8B6B3D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6C4C4C48C714C452E1F2C1E
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202E1F202F2020694820A49987D6D6D6
+      D6D6D6D6D6D6B1ABA176542B3423202D1F202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202E1F202E1F202D
+      1F202C1E202C1E202C1E202C1E202F20203624203A2720442D1F51361E53371D
+      6042216D4D27785A3682674289704D8F7A5D99886E9C8D759F917C9F937FA092
+      7D9F8E76978365886F4C775832593C1E3D29202C1E202E1F202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202C1E203121205A3C1D8C6B3EADA495D3D3D3D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6A8A092704E243222202E1F202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202C1E20402B1F866A42BEBEBED6D6D6D6D6D6D1D1D19883
+      6451361E2B1E202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202E20202E
+      1F202D1F202D1F202C1E202B1E202D1F203020203222203B2720442D1F462F1F
+      53371D5E3F1E6448277355307F633E8E7552937E609D8E76A99E8CAAA59CB9B7
+      B2BFBFBFC7C7C7D1D1D1D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D3D3D3C7
+      C7C7B6B3AFA2937C83663D5A3C1D3323202D1E202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202E1F202D1E20462F1F79562AA08E72C5C5C5D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6CECECE947C5A4F351E2C1E202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202D1E205E401EA2947CD6D6D6D6D6D6D6D6D6B2B0AA7758303624202D1F202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202E20202E1F202D1F202C1E202C1E202C1E202D1E203322203726203C2820
+      4A311F50351E5A3D1F6A4A2570522F7E623C866C48917B5D9B8B72A09583B0A8
+      9BB3B0AABCBCBCCACACAD1D1D1D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      BCBAB79F88646746203423202D1F202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202E20202C
+      1E2036242065451E987D54B5B3AED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B0ABA375552C
+      3423202D1F202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202D1E203D291F84653BBB
+      B9B7D6D6D6D6D6D6D6D6D69F8D735B3D1D2D1E202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202E20202E1F202D1F202C1E202C1E202E1F20302120
+      392620422C1F49301F573A1D5F41206F50297A5B34856B478D765798876DA799
+      84A8A094B3AFA8BCBBB9C5C5C5D1D1D1D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CACACA9E89
+      696646203021202E1F202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202D1F202F2020
+      51371E8F6D3EADA495D3D3D3D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D1D1D199846654381E2C1E202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202C1E20583B1D9C8A6ED5D5D5D6D6D6D6D6D6
+      BCBBB987683E3E2A1F2D1E202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202E20202E1F202D1E202C1E20
+      2C1E202F1F203423203C28204A311F4E341E5B3D1F6D4E297C5F39866C499580
+      619A8A73A59B89B1ABA0B4B3B0C4C4C4CECECED6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CCCCCCC4C4C4B8B7B5AEABA4ADA79CAFA5
+      94A79C899B8E7A978974A1927CB5B1ABD6D6D6D6D6D6C7C7C79B7F5552361E2C
+      1E202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202E1F202D1F204A311F7A58
+      2AA7987FD0D0D0D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6B4B2AF7F5E333B28202C1E202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202D1F2035242074552FB0ADA7D6D6D6D6D6D6D6D6D6A295805F401F2D1F
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202D1F202D1E203322203E292047301F56391E6445257455
+      2F8469438F78579B8B71A59A89AEA89FBBB9B6C7C7C7D3D3D3D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D3D3D3C8C8C8BABAB9B7B3ABA8A0
+      93A2958097856A8E7655866B48775B3775552D715029604324573B1F573A1D56
+      3A1D67441C987644BAB7B3D6D6D6D6D6D6AEAAA27E5C2E3A27202D1E202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202B1D203B2720765224A48E6BBF
+      BFBFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6A1
+      91795B3E1E2D1E202E20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202C1E205035
+      1E988262D1D1D1D6D6D6D6D6D6C2C2C2896D45422D1F2C1E202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      203E2A205C40247255317C63418C785B968974A8A092AFADABC3C3C3D2D2D2D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D5D5D5D2D2D2CECECEC9C9C9C1C1C1B4B2
+      AFB0ABA2AAA0909C8D779884658B714F80623C725430664723583B1D4D341E4B
+      321F3F2A203625203524203322202E1F202C1E202C1E202B1D202C1E2061401D
+      A18F73D5D5D5D6D6D6D2D2D29984655A3C1D2C1E202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202D1F203523205F3F1D967B54BEBCB9D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6BDBCBB8568403E2A202D1E20
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202D1F20342320725229AFA99FD6D6D6D6
+      D6D6D6D6D6ABA2936C4B233121202E1F202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202E1F203222205A3D2077592E8A
+      734D8C7B609688729A90819F9689A59B8BA69B8AA59A89A49987A39886A29785
+      A19684A19684A19684A19684A19684A19684A19684A29785A19785A19583A596
+      7FA5957DA09077948268907C5F917A598A72508066427859336E4F2B5E41225A
+      3C1D4F351E432D1F3D29203323202F20202D1F202C1E202C1E202D1E202D1F20
+      2D1F202E1F202E20202F20202F20202F20202B1D20422C1F8C6F46C2C2C2D6D6
+      D6D6D6D6AFABA576562E3524202E1F202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202D1E202E1F205A3C1D9D7D4DBBB7AFD6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6A398856745202E1F202E20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202C1E204E341E927853CACACAD6D6D6D6D6D6CBCBCB947B56
+      4B321F2C1E202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20203020203E2A1F4C3521523822543922533820
+      5036204F361F4E351E4E351E4E351E4E351E4E351E4E351E4E351E4E351E4E35
+      1E4E351E4E351E4E351E4E351E4E351E4E351E4E351E4F351D4F351E4F351E4F
+      351E4F351F4E341F432D1F3926203725203322202D1F202C1E202C1E202C1E20
+      2D1E202E1F202E20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202D1F2035242077572EB1ADA6D6D6D6D6D6D6CFCFCF95
+      7D5B4D341E2C1E202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202E1F202E1F2054381E89693CACA59BD6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6C7C7C78C714C49311F2C1E202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202E1F20
+      302020684922A69D8ED6D6D6D6D6D6D6D6D6ABA69C6E4F283222202E1F202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202D1F202C1E202C1E202C1E202C1E202C1E202D1E202D1E
+      202D1E202D1E202D1E202D1E202D1E202D1E202D1E202D1E202D1E202D1E202D
+      1E202D1E202D1E202D1E202C1E202C1E202C1E202C1E202B1E202B1E202B1D20
+      2C1E202D1F202D1F202E1F202E20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202E1F202F2020644623ACA392D6D6D6D6D6D6D6D6D6A79D8B6746212E1F20
+      2E20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202E
+      20202B1D20452E1F805E32ACA395D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6ABA4966D4C2530
+      21202E1F202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202C1E20432D1F8B7049C4C4
+      C4D6D6D6D6D6D6CFCFCF96805F52371E2B1D202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202C1E20
+      583C1E9D8F7CD6D6D6D6D6D6D6D6D6B8B7B58061393B28202D1F202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202B1E20
+      432E1F815E2FAEA391D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6CECECE937C5A4C331E2C1E202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202E20202F2020654521A79A86D6D6D6D6D6D6D6D6D6B1
+      AEA97A592E3826202D1F202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202C1E20593B1D9A8A71D6D6
+      D6D6D6D6D6D6D6D0D0D0937D5D4E341E2B1E202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202B1E20432D1F8460
+      2EAFA390D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6AFABA477562C3625202E1F202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202C1E20402A1F85673EBDBCBBD6D6D6D6D6D6D5D5D59E8C70573A1E2D1E20
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202C1E20593B1D9A896FD6D6D6D6D6D6D6D6D6D6
+      D6D6A196845C3E202D1F202E20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202B1D20422C1F84602DAEA390D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D0D0D0A692
+      725A3C1D2C1E202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202C1E2057391D
+      9B8B73D5D5D5D6D6D6D6D6D6B8B7B58062393C28202D1E202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202C1E20593B1D9B8A71D6D6D6D6D6D6D6D6D6D6D6D6B2AEA873552E
+      3322202E1F202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202B1D20422C1F84602DADA392D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6BCBAB7BA904D4E341E281B202D
+      1F202E20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202D1F203826207C5D35B5B3AFD6D6D6D6D6
+      D6D6D6D69E917C63431E2D1F202E20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202C1E20
+      593C1E9F927ED6D6D6D6D6D6D6D6D6D6D6D6C4C4C4836A48402B202C1E202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202C1D20422D1F815E30ACA396D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6C8C4BEBB99646F4C1F4A311F3A27202E20202C1E20
+      2D1F202E20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202C1E2055391E9C886AD3D3D3D6D6D6D6D6D6C1C1C093744844
+      2E1F2C1E202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202E1F202F1F20664725ADA597D6D6
+      D6D6D6D6D6D6D6D6D6D6D3D3D39784684D331E2C1E202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202B1D20442D1F7F5F33ABA59CD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D3D3D3BCBCBCA79E8F9480627C5F3A6143224C321F3A27202F20202C1E
+      202D1E202E1F202F20202F20202F20202F20202F20202F20202F20202D1F2037
+      252078572CAEAAA3D6D6D6D6D6D6D6D6D6BDB3A3A2712A3524202C1D202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202E1F202C1E202B1D20402B2084653AB9B8B6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6A398865D3E202D1E202E20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202C
+      1D20452E2088693CBAB7AFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D3D3D3BAB9B8A99F8F9480627B5F3A66462354381E3F2A1F3021202B
+      1E202C1E202E1F202F20202F20202F20202F20202B1D204E341E947C5ACDCDCD
+      D6D6D6D6D6D6D6D6D6C3C0B9A6844F5E3F1D2F1F202D1F202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202E1F202D1F202B1E202D1F2035242046
+      2E1F593B1D7E5C2EA39783D4D4D4D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B4B0A9
+      74552E3322202D1F202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202B1D2055391E
+      9B7C4DBDBCBAD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D3D3D3BCBBB9AAA19398846684663D6B4B2453371E3F2A1F302120
+      2C1E202D1E202E1F202D1E203020206C4C26ABA397D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6BABAB89B7F555D3E1D3020202D1E202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202E20202E
+      1F202C1E202C1E202F20203A28204D331E5D3F207355318C734FA09077B3AEA6
+      D0D0D0D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CCCCCC957E5D55381E2C1E
+      202E1F202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202E1F202E1F205B3D1E947B57C3C3
+      C3D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D3D3D3BBBBBAABA29399846584663D6B4B2354381E3F2A1F3121
+      202C1E2049301F927751C9C9C9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6BF
+      BFBF96805F6B49213A27202C1E202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202E20202D1F202C1E202D1F20332320422C1F55391D
+      6747247C603A947E5DA89B87B4B2ADC9C9C9D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B8B5B18F704352371E2C1E202E1F202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202D1F202F202067451DA48F70D1D1D1D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D3D3D3BCBBBBAAA19299846683663D735024754E1B895F25A5
+      9C8CD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6C9C9C9AB997A
+      7956273D29202B1D202E20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202E1F202D1E20
+      2C1D1F2D1E1F37251F4A311F60401D73542C886C489E8B6EAAA295BAB9B8CFCF
+      CFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6B8B3AB8F704351361F2C1E202E1F202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202C1E20382620775325A39785D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D3D3D3BBBAB9AEA495BCA57FC3AF8EC4C4C4D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D3D3D3A69A877E5C304B33
+      1E2D1F202E1F202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202E1F202D1F202C1E202D1D1E331F1B4026185635186C49237E5D
+      36907956A4957FB2AEA5C4C4C4D5D5D5D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6B7B4AF8E724C5E3E1D3423202D1E202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202B1E203E2A20856031B1ACA2D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B5AEA192724354381E2F20202D
+      1F202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202E1F202C1E202C1D1F2F1E
+      1D392219482B175A3B1C5D492D55574B546A6E6E8896ADB7BBD3D2D0D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6BFBFBFA38B666B48203524202B1D202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202C1E
+      204F351E907146BCBBB9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6B8B6B2977D5668461F3625202B1E202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202E1F202D1F202C1D202C1D1E331F1B4127185031185E43235F513B525E5C3F
+      6E872C7AB22783D32090E62795EE4DA3EE8EC0E9CFDDE4DADADAD6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CACACA9E
+      8D73735127412C1F2C1E202E20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202E1F202D1F2061401D9D
+      8867CECECED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6C7C7C7A28F71755125442E1F2B1D202E1F202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202E1F202C1E202C1D1F301E1C3A23194B
+      2D1757391D604B2C59594A4A65703B739B2A7FC52089E11E92EF2797F42F98F2
+      2497F12396F12593F12491EE469DE498C0DFD7DEE2DBDBDBD6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D1D1D1AFA28D866334
+      472F1F2B1D202E1F202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202D1F20342320735025ABA08ED6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D0D0D0AA9E8C8A683954381E3020202C1E202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202E1F202D
+      1E202C1D202D1D1E35211A4328185434196347245F543C526260406F8A2C78B1
+      2284D41F8EEA2495F22D98F32B98F02597EF2395F02893F12B94EE2B94EF2A93
+      EE2896ED2596E92491E950A1E5A9CAE3DEE2E2D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6AFA99D86673D54381E3021
+      202D1E202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202C1D20432D1F876538B4B1ABD6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6B8B4AE9B7F5663421D3926202B1E202E20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202E20202E1F202C1E202C1D1F301E1C3D25184D2F175A3B1D
+      645030595C4F4868763675A12580C9208AE22292EF2797F32998F22997F12896
+      EF2A95ED2995ED2895ED2895ED2995EE2A95ED2A95ED2995EC2995ED2995EC27
+      95EE2292EE3091E672ADDFC2D5E0DDDDDDD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B8B5B09A7F545D3E1D3221202D1E202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202C1E20583A1E977B53C2C2C2D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6C4C4C4
+      A493777D5A2E4C331F2E1F202D1E202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202E1F202D1F202C1E20
+      2D1D1E34201B442A1856361A5E43255F563F5163643D6E8D2B7AB72186D8218F
+      EA2495F22998F32A97F12996EF2995EE2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895ED2F95EE2C94EF
+      1C95ED369AE096BFE0DCDEE1D7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6C1C1C19C86667350253C29202C1E202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202E
+      1F202F20205E401EA08C6DD1D1D1D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D4D4D4B2AB9F9377
+      4C63421D3926202C1D202E1F202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202E20202D1F202C1E202C1D1F311E1C3B23184E30175C3D1E5E4B30595D
+      5047697A3275A52480C91F8BE42393F02797F32A98F22A97F02996EE2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2895ED2895F02992F12791F52990
+      E966ABDCBFD7DFDBDBDBD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6CECECEAB9C8279572A402B202B1D202E20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202D1E20352420
+      775426A69A86D5D5D5D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6C2C2C2A392777C5C3152
+      371E3222202C1E202E1F202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202E1F202D1E202C1E202D1D1E3621
+      1A43281855361A6046265B533F4E62653B70912A7BB92085D7218FEB2596F329
+      98F32997F12996EF2995EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2895ED2B97EA3096E82E91F32B90F62795EE2996E23A9CE199
+      C5E5DFDFDFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D5D5D5A79E8F85643750361E2D1E202D1F202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202C1E203F2A1F826031B1AC
+      A4D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D4D4D4B5B0A7977F5C6D4C25462F1F
+      2F20202C1E202E1F202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202E1F202C1E202C1D1F301E1C3D25184C2E185B3D1E614E32565A5144697A31
+      76A72481CB1F8AE32393F02797F32A98F22997EF2995EE2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2D94ED2D95EB209AE81F9AE82A96EB3092F12B8FF42F90ED84B6DDD9DDDC
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B7
+      B2AA8F7248573A1D3222202D1F202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202C1E2054381E9D825BCDCDCDD6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CBCBCBADA39191754C684620452E1F2E1F
+      202C1E202E1F202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202E20202D1F202C1E202C1D1E34201B452A1754
+      361A5E44255E56404F62653A6E902A7CBA2186D9218FEB2596F32998F32A97F1
+      2996EF2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2794F02494F1
+      2996EB2997E92895EE2993F12C93EF2994F12793EC60A7E2C4D5E2D9D9D9D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B9B9B89E8660
+      6C491F3625202C1D202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202E1F20322220765429B3ADA2D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6C4C4C4A699848A6B406C471F482D19301E1C2B
+      1D202D1F202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202E1F202C
+      1E202C1D1F2F1E1D3B23194E30175C3D1D5F4B2E5A5D5046697A3275A42480CA
+      1F8BE42393F02797F32A98F22997EF2996EE2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2895ED2795EF2B92F22D92F02B96
+      EC2797EA2596ED2695EE2493F32391F0469DE2AECEE3DDDDDDD6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CACACAA390736D4C233D29
+      1F2D1E202E20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202B1E2054381E9E8D74D4D4D4D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6C2C0BD9895886A614A6247274F301738221A2C1D1E
+      2C1E202E1F202E20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202E20202D1F202C1E202C1D1F321F1B4127185132185C4122
+      5F533A5464623D6F8F2B7BB72085D72090EC2595F32998F32A97F12996EF2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED28
+      93F12C95EC2B97EB2594ED3C95E0A2C6E0E0E0E0D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CFCFCFA79A8687633248301F2B1D202E
+      1F202F20202F20202F20202F20202F20202F20202F20202F20202F20202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202B1E204D33
+      1F957F60CBCBCBD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D7D7D7C8DAE6559FE02783D63074A65264626250325A3B1C472B1735201A2E1D
+      1D2C1D1F2D1E202D1F202E1F202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202E1F202D1F202D1E202C1D1F
+      2E1D1D37211A45291755371A5F49295E5B484A667135729D257EC51F89E12292
+      F02797F32A98F22996EF2996EE2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2B94EE2D95EB2A96EB
+      2194F12392F13992E18FBDE2DFE0E0D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6B1AA9D8665394D331E2E1F202E1F202F2020
+      2F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202B1D2051361E9C8768CFCFCFD6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D8D8D8C3D4E0499EE12693
+      EF2296FA1C92F61E8AE12D7DB9416D86565E56604E325F411F5032183D241835
+      201A301E1D2C1D1E2B1D1F2C1D1F2C1E202C1E202C1E202C1E202C1E202C1E20
+      2C1E202C1D1F2B1D1F2C1D1E321F1C3721194026174F31195A3E1F625133565D
+      54426B822F78AF2283D1208EE92495F22898F32A98F12996EF2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2994F02C95ED2B96EC2394EF1E92F42292
+      F32C91E884B8E0E0E0DED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6B2AEA7987A4C5F3F1D3021202C1E202F20202F20202F20
+      202F20202F20202F20202F20202F20202F20202F20202F20202F20202F20202F
+      20202F20202F20202E1F202F202068461EAA9F8DD6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D8D8D8BFD2DF439CE62192EF2896EE2895ED2A97ED2C
+      99EF2997F22091EF1F86DD2A7CBC3D72944E6469585749604D30594125583A1C
+      5939194C2F174227174128174127184127184128184127174328174F31185939
+      1A5A3D1F60482960513755594F4B656F3A7195297CBC2087DB2191ED2696F329
+      98F32997F02996EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2594F12794F02B95ED2B95EC2995ED2795EE2494F02991E87F
+      B5E1DEE0E0D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6C0BFBD9E835A62411C3625202C1E202E1F202F20202F20202F20202F
+      20202F20202F20202F20202F20202F20202F20202F20202F20202F20202C1E20
+      2D1F205C3E1D987C53C3C3C3D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D8
+      D8D8C3D3E0459CE61C94F22996EF2C95EB2D96E92D96EA2895EE2695F02896F1
+      2998F22897F32393F01F8BE52384D3287ABA3477A53E6C8948687650666A5363
+      61565A50585749585749585849535A545062654C666D45687A3B70923178AC27
+      7EC32387D91F8CE72394F12797F32998F22996EF2995EE2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED27
+      95EE2695EF2894EF2E94ED3095EA2C96EB2B97E92395F01F8FF086B8DFDFE0E0
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6C3
+      C3C3A08D7078572C4A311F3322202C1E202C1E202D1E202C1E202C1E202C1E20
+      2C1E202C1E202C1E202C1E202D1E202C1E202C1E203826205F3F1D9A7C4FBEBB
+      B6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D8D8D8C5D5E14C9EE12592F0
+      2A95EE2A96EA2A95EE2C95EC2A95ED2593F12494F12A96EC2A96EB2795ED2896
+      EF2A97F02C98F22A98F22796F22192F01F8EEB208DE7208CE41F86DB1E83D51E
+      82D51E82D52088DD208DE6208EE81F8FEC2293F12797F22998F22A98F22B97EF
+      2A96EE2A95EE2A94EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2B96EB2995EE2793F1
+      2C93EF2E95EC2A95ED2595EE2996EC2896EE2A91E989BBE3DFE0E0D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D3D3D3AFA89C
+      91795675532B5A3B1D442E1F3A28203826203725203725203725203725203725
+      203826203B2720402B1F573B1D826134A38F6FBCBCBCD6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6DADADAC8D9E3579FDE2591ED2896ED2C95EC2995ED2995
+      ED2995ED2995ED2895EE2895EE2995ED2B96E92396EE2195F12995EC2F95EA2F
+      94ED2F94ED2996EE2997EF2A97F02A97F02A98F12A98F12A98F12A98F12A98F0
+      2A97F02997EF2996EF2A96ED2897EA2696ED2B94EE2D95EB2997EA2B94EE2B93
+      EF2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995EE2995ED2A95ED2995
+      ED2894EF2A93F02C95EE2493F43091E794C0E1E0E0E0D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CACACAB2ACA1A08E
+      738C724B8160338C632B9065289165299166299165299065288E642B83613385
+      6C48A08F75B9B5ADD3D3D3D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D2DDE262A5DE2C91EB2194F52896EC2D96EA2B95EC2995ED2995ED2995ED29
+      95ED2995ED2995ED2F94EC2D93EF2493F22494F12495EF1F93F41D92F62796EE
+      2796EC2796EC2796EC2796ED2796ED2796ED2796ED2796ED2796ED2796EC2796
+      EC2696ED1D95F01F93F42193F42295EF2497EC2A96EC2A95ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995EE2D94ED2C
+      95EC2493F22392F53F93E1A4C8E4DBDBDBD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6C5C5C5B6B3AFB8
+      B0A2BDB2A0C3B6A0C5B8A1C4B6A0BEB3A0B8B0A2B6B4AFC5C5C5D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DBDEDF73AEDF2990EA21
+      94F62194F22A96EB2D96EA2A95ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2D93F03292F02A94F02694EF2B92E42D92E22791EB2892EC2792EC2791EC2791
+      EC2792EC2792EC2791EC2791EC2792EC2792EC2792EC2891EC2692EA2A92E332
+      90E62892EC1E96F12395EF2A95EC2996EC2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2D95EB2D96EA2695EF1F93F6
+      2691F23F95E3B6D0E3D9D9D9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6E0E0DF84B9E42692E92796EF2495EE2895EC2B96EB
+      2895EE2594F12995ED2995ED2995ED2995ED2995ED2995ED2393F22C94ED2B96
+      EA2E94E870B8E79CC5DE9AC1DF9FC1DD9FC2DC9FC2DD9FC2DD9FC2DD9FC2DD9F
+      C2DD9FC2DD9FC2DD9FC2DD9FC2DCA0C2DC9BC2DE96C3DF9FC3E360A9E32694E9
+      2892F42C94F12895ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2895ED2795EE2B96EB2996EC2594EF2693F12891EC4C9C
+      E4CCD8E1D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      DADADAA1CDDC2592E62293F42C96E92D97E82D96E92A95EC2695F02494F12995
+      ED2995ED2995ED2995ED2995ED2995ED1F94F22B96EA2797E92F93E7AFD7E9DF
+      DFDFDADADAD7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7
+      D7D7D7D7D7D7D7D7D7D9D9D9DBDBDBDEDEDEAACEE53094DF2C92F52D92F42795
+      EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2895EE2595EF2795EE2B95EC2D96EA2E96E92B96EC2390F36EADDDDBDDDCD6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7C1D6E23B99E12293
+      EF2794F02895EE2E95EB2C95EC2895EF2795EE2A95EC2995ED2995ED2995ED29
+      95ED2995ED2995ED2593F13395E82396EF238FF19AC4DED6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6C4D9E03A9DDE2B91F32B93F02596EC2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A95ED2B95ED26
+      94F02694F02C96EC2C96EB2695EE2496F12991E98ABAE2DFDFDFD6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D8DDE159A6E62E8FEE2A98EB1E96EE2794EF2B
+      95ED2994EE2894EF2995ED2A96EB2995ED2995ED2995ED2995ED2995ED2995ED
+      2994EF3094EC2894EE1E8FF676B4E3DBDBDBD6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D7DFDE54A8E02690F32894EF2797EB2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2A95EC2B95EC2894EF2794F02995EE
+      2895ED2595EF2395EF2E92F03090E89ECBE6DADADAD6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6DBDBDB8CBDDD2891E82D94F02997EB2696EC2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895EE2A94EF2C95
+      EC2392F35FA8E3DADCDCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DBDBDB78B6E3
+      258FF02794F02997EB2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2594
+      F23194ED2893ED429EE1CADBE3D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D8D8D8B8CEDE319ADE
+      269BE62293F33192F03495EA2895ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2695ED2E94EE2796EC2593F3489EE1CD
+      D8DCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D9D9D993C5E72A90EA2794F22A97
+      EB2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2A95ED2E95ED2495EF2B97E92B
+      91E770AFDFDBDFDFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D2DDE254A2E22493EC219AE82593F13190
+      F12E95EC2895ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2595EE3094ED2696EC2793F23195E2B1CCDFD6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D8D8D8B1D1E23494E52694F12C96EC2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED3094EE2497EA2597EA2592F52A91E69DC9E1
+      DBDBDBD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6DDDDDD83BADE2290EE2D93F02D93EE2995EB2694EF2694F12995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2595F02E95EC2896ED2595F12992E68FBDE1DADADAD6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D1DBE04B9FE12593EE2D95ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2B93EF2C97E72196EE2492F31F93F2449CE3CDDDE5D7D7D7D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DADADAB9D1E33A
+      96E22394F02892EF3692EF2898E71F96EF2793F12995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2794F02897EA2E94
+      ED1F97EE2892EB6BADE4DDDEDED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DDDDDD
+      61ACE22593ED2E94ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2994
+      EE2994EE2895EE2896E92394F3268FED79B5DFE0E2E2D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7E5E763A3E12891ED2697E72693F3
+      2C95EB2898E72694EF2994EF2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2A94EF2697EA2F94ED1E97EC2992F052
+      A2E5D5DCDED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DADADA7EBBE42491EB2E94
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A96EB2491F72E95EB2D
+      9AE32594F12790F33896DFBDD5E0D8D8D8D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6DADADA9FCAE32E8EEC2794EF2996EC2995EE2895EC2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED3095EA2B94ED2197EE2B96EB2792F3389BE3C3D4DFD6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6AFC8DE2C92ED2695EF2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2894EF2A95ED2A96EC2894ED2995EE
+      2391ED65A8E3DDE0DFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D4DD
+      E055A4DB2892EF2995EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2E95EC2A94EF2297EC3095E82894F22895E5AAC8DDD6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6C7D4DF3E99E82595EF2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895EE2E92E2A1C8
+      E2DCDCDCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D8D8D897C3E02A93EA2897ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2B94EF2694F02697
+      EA3395E92794EF2293EA8AB9E3D9D9D9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D5DDE052A1E32393EF2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2A96EA2496EB46A0E5D1DFE5D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D7DADD56A4E32691F32797ED2996ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2A93F12495EE2A97E93294EA2896EE22
+      94EE68ABE7DCDCDCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DEDEDE70AFE02292
+      EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2996EC2696EE228FEE93C3E7DCDCDCD6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D8D8D8A1C5DF2D91EC2B93F22796ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2B93F12497EB2A96EA2D93F12696EC2796EC489DE8D3DBE1
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D8D8D894BFDF2290EC2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2794EE2692F053A0E5D8DEE1D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D5DCE04FA6E02992
+      F22A94EF2797EC2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2F93EF2598E82995EC2791F82497EB2E98E73295E7BBD2E3D8D8D8D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6B0CDE32892EF2995ED2995EE2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995EC2795ED2694
+      F03191E3A1C6E0D9D9D9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D8D8D8A3CAE32793E82C94F12694F02896EB29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2C94ED2897EA2995
+      EE2792F32797ED2D97E92A93EA98C2E4D9D9D9D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6C1D6E33597E82B96EB2A97E92493F22993F12797EA2C95EC2A95ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2A95EC2495EF2795EC2B92ED53A6E8DA
+      DEDED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6DAE1E25EABDE2890EF2A93F02595EF2B95EC2A95ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995EC2995ED29
+      95ED2592EE7EB5E6D9D9D9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6E1E44AA1
+      E32994EA2A98E42191F42B92F22499E72D94EC2B94EE2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2595EF2D96EA2592F52793EDB9CFDCD6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B1D0E1
+      3296DF2B93EF2894F02897EB2995EC2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2694EF67A9E1
+      DCDDDCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6E0E0E06FB1E52391EC2897E929
+      93F02E94ED2498E92993F02A94EE2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2795EE2B95EC2E94ED2595EF2692EF7CB3DEDCDCDCD6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DBDBDB7AB6E22092ED2B94EF2695
+      EF2898E82896ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2694EF459AE2CAD9E0D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D7D7D79EC2E32791EB2496ED3095ED2F96E92397EC
+      2893F22A94EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2696ED2B95EC2F94
+      ED2696EB2692F23D9AE7C9DAE0D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D0DADF449FE51C92F52D94EF2695ED2698E92995EE29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2795EE3194E5AACDE4D9D9D9D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6BECFDF3999E52295F03295EC2C97E82496EE2A93F02A94EE2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2896EC2894EF2F94EE2896EA2793F224
+      91EB93C2E2D9D9D9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7
+      D7D79DC4E22D95E32593F02D92F12997EA2397EC2A94F02995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2895ED2491E988BDE6DCDCDCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D2D8
+      DE4CA1DE2295F13294EC2697EA2595EF2F94EC2A95EC2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2795EE2C94EE2993F02996EA2394ED5AA7E3DADEDE
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DCDCDC6BAEE92B94E5
+      2F95EC2C91F22B97E92296EE2C93F02A95ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2996EC2192ED
+      70AFE4DCDCDCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DCDDDD66ACDD2294F12F
+      93EE2397EC2594F03195EA2996EC2895ED2895ED2995ED2995ED2A95ED2A95ED
+      2A95ED2995ED2896EC2996EC2A95EC2A95ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2A95ED2A95ED2995ED2995EC2995EC2995EC2895ED2895EE2995ED28
+      95EE2895EE2895ED2995EC2995EC2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2694F02896EC2895EE2991F32C9AE52797ED3797E7BED4E1D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CCD9E1439DE82794EC2B95ED2A95EE2A96
+      EC2895EE2A95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2B95ED2A94EE52A5E5D4DCDED6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D9D9D983B7E42191F02796EC2796EC2794F0
+      2A95EC2596EC2496ED2695ED2A95ED2B95EC2B95ED2D94ED2D94ED2896EB2498
+      EA2498EA2996EC2B94EF2995ED2995ED2995ED2995ED2995ED2995ED2A95ED2A
+      94EE2795EF2896EC2B97E82896EB2195F02394F12895EE2795EF2294F22295F0
+      2995EC2C96EA2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895EE2A95ED2995
+      ED2994EE2A97ED2997EF2792EA8FBDE4D9D9D9D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D7D7D7A3C7DF2C94E62996EF2894EC2894EC2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2A95EC2B95EE3B9CE6C3D5DED6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D8D8D899C2E72891EB2597EB2896EC2694F02A94ED3193F03193
+      EF3194EC2F95EB2A97EA2397EB2397EC2696EC2B95ED2D93EF2B93EF2494F120
+      94F32795EE2995ED2995ED2995ED2995ED2995ED2895EC2696EC2395EF2695EE
+      2B95ED2C94EF2A93F12995ED2C97E92C96EA2895ED2694F02795EF2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A
+      96EE2593EC5FA7E2DBDEDED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DCDCDC72
+      B2DE2394EA2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2996EC2A95EE2E94E7A9CADFD7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6B8D0E23896E72896EC2895ED2694F02B95ED2A93F12C94EF2E94EC2F95EB2B
+      95EC2894F02496EE2697EA2E94EC3492EE3293EE2B95ED2597EB2895ED2995ED
+      2995ED2995ED2995ED2995ED2997EA2898E62897E92895ED2A94EF2E93EE3194
+      EC2A96EC2996EB2B96EA2D96EB2C94ED2794F12693F12995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895EE389AE5
+      C1D5E2D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D3D9DF489FE32196EE2A95ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995EC2B96EF
+      2A92EB89BCE0DADADAD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D0DCE14D9FE028
+      95EB2795ED2694F02A95EC2396ED2296ED2296ED2495EF2794F42890F42893F2
+      2494F02495F02295EF2495ED2A96EB2D96E92A95EC2995ED2995ED2995ED2995
+      ED2995ED2594EF2395EF2996EC2895F02194F42094F32597EE2294EF1F93F421
+      94F42A95EE3095EA2C94EE2894F02995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2895ED2294EB96C2E2D9D9D9D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6B5CCE42C94EA2395EE2A95ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2896EC2996ED2891EF61AAE3DCDF
+      DFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DDDEDE6DADE02993E92695EF2594F1
+      2D95EB3295EA2B95ED2295F12096F02695EA3195E13994E23894E83797E52D95
+      E22493E62495EF2593F32894EE2995ED2995ED2995ED2995ED2A95EC2293F521
+      90F53593E63E96E23596E92E97EA2F97E22E93E12693F21D92F82694F03096E9
+      2E95EC2895EF2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2895ED2092ED6DB0E9DDDDDDD6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6DCDCDC89BBE32390EE2996ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2796EB2896EC2891F4489FE6D2DBDED6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6DADADA83BAE42B93E82694F12594F22E95EB3295EA2994
+      F02493F42E94E756A3DEA9CEE2BBD4E0B9D1E0BFD1DCBAD1DF8ABDE33E99E123
+      93EC2795EE2995ED2995ED2995ED2995ED2895ED2E93E95AA5E7AECFE4C2D2DC
+      BCCFDDBAD1DEB7D1DF85BCDF4097DF2692F12694F02F96E92A96EB2595F02995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2993ED4B9FE7D3DCE0D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DEE1E15CAAE723
+      92EE2C96EB2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2796ED2995EE2495EF3999EAC6D2DED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D7D7D7A5C9E22792EC2793F22893F12C94EE2794F02894EF2A93EF3894E7B8
+      D4E9DCDCDCD7D7D7D6D6D6D6D6D6D7D7D7E0E0E0B7D2E4409BE02695EE2995EC
+      2A96EC2A96EC2996ED2494EF3D99E0C4DCE8DBDBDBD6D6D6D6D6D6D6D6D6D7D7
+      D7E3E5E599CAE32C95E22693F22696EC2394F12894F02B94EE2B94EE2B94EE2B
+      94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2994EF2594F0
+      2695EF2895ED2995ED2A95ED2A95EE2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2B94EE
+      3793EABAD2E0D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D2DCE0419EEE2594EE2D96E92995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895EF2994EE
+      2599EA2990ECA7C3E3DADADAD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6C8D4DF36
+      98EC2894F02996EB2696ED2395F02D95EA2E95E93198ECC8DAE3D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6DBDBDB74B2E42292EE2794EE2994EC2A95ED2995
+      F02192F24DA3E6D7DEDFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DCE1E161
+      AAE12D90F02A95EC2894F02C96EB2996EC2996EC2996EC2996EC2996EC2996EC
+      2996EC2996EC2996EC2996EC2996EC2996EC2895ED2795EF2896ED2996EB2B96
+      EC2A94EE2A93F02994EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2794F02E8EEE99C3E0D8D8
+      D8D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6C1D3DE3596E82796F02C96EB2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2895EF2B94EE2B96EB2392EB7AB3
+      DFDDDDDDD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7DCDF55A2E62C94EB2998E8
+      2898E72895EE2D94EE2B93F12890F0B4CEE0D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D9D9D993C2E42992EA2A94F02D94ED2E94EC2D94ED2A94EF4398E5C6
+      D3E1D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DADADA85B7E32992E83195EA
+      2A97E92797EB2A93F02A93F02A93EF2A93EF2A93EF2A93F02A93F02A93EF2A93
+      EF2A93F02A93F02A93F02C94ED2F96E92D96E92C96EA2B96EC2894EE2793F128
+      94EF2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2595EE2A91ED7CB5DDDBDBDBD6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D9D9
+      D9A3C7DE2B93E82697EF2B96EB2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2894EF2D95EE3293ED2295EC57ABDEDDDDDED6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6DEDFDF6AB0E32D91EC2A94EE2C95EC2997EB2895
+      ED2493F31F90F096BFDDD9D9D9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7B7
+      D1E22E93E92394F32795F22894EF2893EE2895EF2D91E9A2C6E4D9D9D9D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D8D8D89CC8E32196E33093EF2C95EC2395F02895
+      EE2895EE2795EE2795EE2795EE2895EE2895EE2795EE2795EE2895EE2895EE28
+      95EE2895EF2995EF2895EF2695EE2895EE2995ED2A95EE2A95ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2395EE2693EF5EA8DEDADEE0D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DDDDDD7FB7E12793E923
+      95ED2996EC2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2895EE2A95EC3092EF2295EE369EE4CCDAE3D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6DCDCDC8AC0E12791ED1B95F61D95F12C96EA2D94ED2993F02792F078
+      B0E1DCDCDCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D2DADF489FE42696EC
+      2895EB2796EC2996EB2996EB2592E970B8E2DFE0E0D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6BED3E03699E12992F32794F12192F42E94ED2E94ED2D94ED2D
+      94ED2D94ED2E94ED2E94ED2D94ED2D94ED2E94ED2E94ED2E94EC2B94F02393F6
+      2294F42295F22695EF2A95EC2F96EB2B95EC2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2496ED
+      2594F04A9FE2CCD8DED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6DDDEDE66ACE62593E92397ED2896ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895EE
+      2894EF2894F12B93E7A9CFE3D8D8D8D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7B7
+      D0E03693E22395E82498E42A93EB2891EF2891EF2590EE4E9FE4D7DDDFD6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DCDDDD5FA8E22791E82792ED2392EF2492
+      EE2492EE2991EA66ACD7DCDFDFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D8
+      DDDE55A3DC1C95E52498E02294EC2892ED2892ED2792ED2792ED2792ED2892ED
+      2892ED2792ED2792ED2892ED2892ED2792ED2C93EA3293E62B93E82694EC2694
+      EE2895ED2B96EC2B95EC2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2796ED2796F03C98E4BFD1
+      DCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D8DCDD56A4E52495EA2397ED2895ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2A96EB2595EE2396EF2C92F12F90
+      EB8BC2E2DADADAD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D3D8DB95BADE86B7DE
+      8AB8D985B8DD83B8DE85B8DC80B8DC91BEDDD5D8D9D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D8D8D8AECADD86B6D986B7DD84B6E084B7E082B7DF82B5E2A1
+      C0D8D8D9D8D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D9D9D9A2C1DE6BB4DF
+      87B8D988B7DD83B8DC82B9DC82B9DC82B9DC82B9DC82B9DC82B9DC82B9DC82B9
+      DC82B9DC82B9DC83B8DC86B9DC84B7DA72B3E059A9E53E9CE62A93E82693EB25
+      95EF2995EE2995EC2995ED2795ED2895ED2A95EC2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995EE2995F02F95E9AFCBDED6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D3D8
+      DE48A0EA2396EC2994F02B94ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2B95EC2796EC2394F22D95EB2A90EF70B2E7DADADAD6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D9D9D9D9D9D9D9D9D9D9D9D9D9D9
+      D9D9D9D9D9D9D9D8D8D8D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D7D7D7D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DADADADEDEDEDADADAD9D9D9D9D9
+      D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9
+      D9D9D9D9D9DADADADDDDDDDBDFE0CADAE39FC7E26EACDE3298E42592EC2993EE
+      2796EB2197EC2396ED2D95EC2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2895F22C94EE2394E98DBFE2D7D7D7D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6C5D0DD3898EA2296EC2A
+      93F12B94ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2B95EC2996EC2593F32E96E92892F05BA6E8DBDCDCD6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6DADADADFDFDFBBCFDD65A9DF2C8DF12594EF2B99E52F95
+      EC2892F32995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2794F1
+      2D95ED2295EA81B9E5D8D8D8D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6BACDDE2F95E62496ED2893F02A95ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A95ED2895ED
+      2794F02D95E92694F13F98E5CBD8DFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6DBDEDF86BAE52F91E92C95EA2D95EC2891F42895ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2794EF2C97ED2294ED75B3
+      E5DADADAD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D7D7D7AAC8E02C94E42895EF2894EF2895EC2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2895ED2795EF2A95EE2D96EB2495
+      F02F90E6A9CBE4D9D9D9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6DFDFDF90C5E72D92E72493F22694EF2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2895EF2997EB2392F15DA6E3DDE0DFD6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DADADA8EBE
+      E62A96E32B94EE2894EF2796EC2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2795ED2694F02C95EB2C96EB2395F0278FE982B9E6DD
+      DDDDD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      DFDFDF8ABFE4288FEA2497ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2896EE2797E92491F34F9FE5D4E1E4D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DADADA7FB8E82A96E12D94EF26
+      95EF2696EC2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2795EE2593F23095E92B95EC2095EE2592EF66AAE4DEDEDDD6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DFE1E16CB0
+      DE2493E92995EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A95ED
+      2699E72591F6499BE4CBDDE5D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6DADADA7FB8E82895E42D95ED2795EF2796ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895EE2793F1
+      2D95EC2995EE2395EE2993EF54A1E2D5DBDDD6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D8D8D8C4DEE93899E12294EF2B
+      95EE2896EB2893EF2996EB2895EE2A94EF2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2996EC2897EA2692F44199
+      E6C5DAE5D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6DADADA80B7E52593EB2997E92A94EE2894EF2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2B95EC2895EF2594F02794
+      EE2F96EA3E9AE2BFD2E1D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6E0E3E381B8E12191ED2C94EF2898EA2793F1
+      2997EA2794EF2C92F12995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2795ED2B94EF2994EE389AE7C1D6E5D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DADADA7FB5
+      E52593EB2A97E92A94EE2894EF2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2B96EB2896EC2595EF2796EF2996EB2E95E6A9
+      C7E0D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6C5D5E23B98E52B94EF2896EA2794F12B96EC2795EE2B93
+      F02995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2795ED2B94EF2994EE3698E5C0D4E4D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DADADA7DB5E32593EB2A97E92A
+      94EE2894EF2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2996EC2B97EA2896EB2595EE2596F02697EF2493E988B9E1DADADAD6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6DDDDDD68ABE02892ED2997EB2794EF2C94EE2796ED2A94EE2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2795ED
+      2B94EF2995EE3597E4BED2E2D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6DADADA7DB4E32593EB2A97E92A94EE2894EF2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A96EB
+      2896EB2696ED2696EF2696EF2193EC61AAE3DBDFE0D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D9D9D98FBEE127
+      92EB2896EA2695EE2C93EF2996EC2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2795ED2A93EE2995EE3698
+      E5BFD2E2D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6DADADA7FB5E52593EB2997E92A94EE2894EF2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895EE2995ED2895ED2794
+      EC2795ED2694EF409EE6CCDAE1D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6ADCBE02894EC2996EA2696EC
+      2D92F22996EB2896EC2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2795ED2A93EE2995EE3799E6C0D4E4D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DADADA80B7
+      E52693EB2996E92A94EE2894EF2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2795F02894EF2B95EE2B95EC2994EB2A95EE30
+      99E9BAD0DFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D9D9D9DADA
+      DADADADAD9D9D9D7D7D7D8D8D8D8D8D8D8D8D8D9D9D9D9D9D9D9D9D9D9D9D9D9
+      D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9
+      D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D9D8D8D8D7D7D7D8D8
+      D8DADADAD7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6BAD0DE3097EC2A97EC2596EC2E91F22997EA2796
+      EB2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2795ED2A93EE2994EE399AE7C1D5E4D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DADADA7DB5E82E93E52495F028
+      93EF2C95EC2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995EE2995ED2995ED2A95ED2995ED2995EE2393EA96C3E1D9D9D9
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D8D8D9A2C4DE8EBFE48EC2E392BFE4A1
+      C1DF99C3DE9AC0E196C1E196C1E296C0E296C0E295C0E295C0E296C0E296C0E2
+      96C0E295C0E295C0E296C0E296C0E295C0E295C0E296C0E296C0E295C0E295C0
+      E295C0E296C0E296C0E295C0E296C0E297C1E19DC1DE9AC1DF90C0E2A0C6DDBE
+      D3DDDCDFDED7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6BACFE13296EA2597EC2996EC2B93F02895EE2A96EB2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A95ED
+      2496EC2994EE439CE4C6D7E7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6DADADA80B6E93194E52195F12794F02D95EB2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2392EB71B4E2DEDEDED6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6DBDCDC56A4DE2391E92094E52291EE3092EA2696E42893EA
+      2694E82693E92694E82694E82693E92693E92694E82694E82694E92693E92693
+      E92694E82694E82693E92693E92694E82694E82693E92693E92693E92694E826
+      94E82693E92693E92694E92994E72A92E72492E92793E73C98DE70ACDBABCFE6
+      DEE0E0D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B8CFE32F
+      95EA2497EC2A96EC2A93F02894EF2B96EB2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2B95EE2497EC2994EF4C9F
+      E1CDD9E4D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6DADADA8DBCE73192E62395F02794F02C95EC2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2694EB5AA7E1DBDDDED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      DBDBDB7BB6E32692ED2595EE2594F32E93F12997EC2A95F02895EF2895F02895
+      F02895F02895F02895F02895F02895F02895F02895F02895F02895F02895F028
+      95F02895F02895F02895F02895F02895F02895F02895F02895F02895F02895F0
+      2895F02A95EF2A93EE2793F32692F72891F52B90ED3694E79BC5E5DBDBDBD6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B8CFE32F95EA2497EC2A96EC
+      2A93F02894EF2B96EB2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2A95EE2597EB2893F059A6E1D7DBDED6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D9D9D9A4C8
+      E42F91E62595EF2694EF2B95EC2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED28
+      95ED419DE6CBD6DFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D79FC4DC2E94
+      E52D96EA2996E92E95EC2D97E72D97EA2C97EA2C96EA2C97EA2C97EA2C96EA2C
+      96EA2C97EA2C97EA2C97EA2C96EA2C96EA2C97EA2C97EA2C96EA2C96EA2C97EA
+      2C97EA2C96EA2C96EA2C96EA2C97EA2C97EA2C96EA2C96EA2C96EA2B97E92997
+      E92B96EC2C93ED2B95EE2797EC2297E93B99E0BDD3DFD7D7D7D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6B8CFE32F95EA2497EC2A96EC2A93F02894EF2B96
+      EB2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2994EF2798EA2692F062ABE3DDDDDCD6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B8CFDF3193E92695EE26
+      95EE2A95EC2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895EE2892E8ABCDE4
+      D9D9D9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B9D1DF3296E92C94F02495ED25
+      93F32895EE2795EE2694F02694F02694F02694F02694F02694F02694F02694F0
+      2694F02694F02694F02694F02694F02694F02694F02694F02694F02694F02694
+      F02694F02694F02694F02694F02694F02694F02395F02196F02594EF2A95EE2B
+      95EC2797EA2398EA1C95E962B1D9DCDFDFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6B8CFE32F95EA2497EC2A96EC2A93F02894EF2B96EB2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2894EF
+      2898EA2591F076B4E5DADADAD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6C1D3E13696EC2895ED2595EE2995EC2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2694F02292EB8CBDE3DBDBDBD6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6CFDAE0439BEB3492EF2A96EC2791F52D93EF2B94EE
+      2B93F12B93F12B93F12B93F12B93F12B93F12B93F12B93F12B93F12B93F12B93
+      F12B93F12B93F12B93F12B93F12B93F12B93F12B93F12B93F12B93F12B93F12B
+      93F12B93F12B93F12B93F12B93F12B92F22C93F02D93EF2D94EE2C93F02B93F1
+      2596EF379EDFC7D5DED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B8CFE32F
+      95EA2497EC2A96EC2A93F02894EF2B96EB2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2894F02A98E82591F17FB9
+      E7D8D8D8D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D2DBE0469FEB2895EC2795EE2895ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2893EF2792ED6DAEE2DCDCDCD6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6DADEDF5FAAE42893EB2499E62894EF2896EB2998E82897EB2897EB2897
+      EB2897EB2897EB2897EB2897EB2897EB2897EB2897EB2897EB2897EB2897EB28
+      97EB2897EB2897EB2897EB2897EB2897EB2897EB2897EB2897EB2897EB2897EB
+      2897EB2A96EB2B95EC2A96EB2896EC2796EC2695EE2694F02C96EE3096E8B1CC
+      E0D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B8CFE32F95EA2497EC2A96EC
+      2A93F02894EF2B96EB2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2894ED2997EB2590EF92BFE4D7D7D7D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DADB
+      DC52A4E42A95EE2B93EE2596EE2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2C
+      95EC3093EC469FE9D6DBE1D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DADADA8BB7
+      E01D92F02298EA2F92F12495F02A97EA2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2895ED2A93E8A3C9E3D7D7D7D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6B8CFE32F95EA2497EC2A96EC2A93F02894EF2B96
+      EB2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2897EA2694F13295EAB4CADFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DDDDDD67AEE32992EB2B
+      93EF2595ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2796ED2F94EF3999E5
+      C1D2DFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7A7C6E12593E92597EB2B
+      93F02894F02897EA2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2895ED2A93E8A3C9E3D7D7D7D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6B8CFE32F95EA2497EC2A96EC2A93F02894EF2B96EB2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2696EB
+      2693F03E99E5C1D2DED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D9D9D988BDDD2A91E92A93F02696ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2496ED2C94EE3095E5A5C9E2D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6C1D3E03897E42697EC2795EF2C93F12697EB
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2895ED2A93E8A3C9E3D7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B8CFE32F
+      95EA2497EC2A96EC2A93F02894EF2B96EB2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2595EC2793F04DA0E3D0DA
+      E0D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D7D7D7A6C9DC2E92EA2B94F12896EB2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2496ED2A97ED2A93E882BAE4D9D9D9D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D7DCDE57A3E12596EC2795ED2C93F12796EC2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2996EE2A93E8A3C9
+      E3D7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B8CFE32F95EA2497EC2A96EC
+      2A93F02894EF2B96EB2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2596ED2793F065AAE0DBDEDED6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6BED3DC3897EB2893EF2997EB2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95EF2798EA2892ED61A9E5DCDDDDD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DADA
+      DA7AB1E12195EB2A96EB2993F22B95ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2996EE2A93E8A3C9E3D7D7D7D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6B8CFE32F95EA2497EC2A96EC2A93F02894EF2B96
+      EB2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2597EF2A91EE7FB6E2DADADAD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CFDDE1439DEE29
+      91F02996EA2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2C93F02299E72892F2
+      4A9DE3D4DDE1D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D794BFE51F94E82D
+      96EA2593F32B94EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2A96EE2A92E8A2C9E3D7D7D7D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6B8CFE32F95EA2497EC2A96EC2A93F02894EF2B96EB2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2395EE
+      298FEC94C2E6D9D9D9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DDE0E063ABE82592EE2B96E92995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2594F22195F02996EC3A95D9A7D1E8DEE3
+      E1DDDDDBDCDDDDDBDDDEDBDDDEDBDDDEDBDDDEDBDDDEDBDDDEDBDDDEDBDDDEDB
+      DDDEDBDDDEDBDDDEDCDDDDDDDDDBDEE4E398C9EC3193DF2996EB2195F22695EF
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2A95EE2B94EF2B95EE2C95EC2B95EB2B95EC2A96EC
+      2E96E82C93E79FC8E6D7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B8CFE32F
+      95EA2497EC2A96EC2A93F02894EF2B96EB2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2894EE3597E9BAD2E0D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6DADADA91BCE42291EA2897EC2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2C94EE2C93F02695EF2396EB309DEA53AAE561ACE15DAAE65A
+      A8E75AA8E65AA8E75AA8E65AA8E75AA8E75AA8E75AA8E75AA8E75AA8E65AA8E7
+      5CA8E660A8E552A8EB339DEF2595ED2696EC2B97E72C97E92995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2A95EE2B94EE2B95ED2B95EC2A95EC2996ED2895ED2595EE2F94E6ADCC
+      E4D7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B8CFE32F95EA2497EC2A96EC
+      2A93F02894EF2B96EB2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2493EE4DA1E5D5DDDFD6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6BACEE32B95E82196ED2A95ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2C
+      95EC2E94ED2B93F12893F42490F42391F22593EF2694EC2694EC2694EC2694EC
+      2694EC2694EC2694EC2694EC2694EC2694EC2694EC2694EC2794ED2592EF2392
+      EF2492F02893F22B92F22E91F32C93F02995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A95ED2C95
+      ED2A95ED2895EE2695EF2694F02695F11793F83E9AE0CDD6DED6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6B8CFE32F95EA2497EC2A96EC2A93F02894EF2B96
+      EB2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2895ED1F93ED70B0E5DCDCDCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DAE049
+      A2E41F95ED2A95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2696EC2696EC2B95EC
+      3195EC2F95E92C98E82898E62896EB2896EB2896EB2896EB2896EB2896EB2896
+      EB2896EB2896EB2896EB2896EB2896EB2896EB2996EC2B97E82F98E53097E62B
+      96EA2695EF2794EF2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2A95ED2C95EB2A95ED2694F025
+      94F02794EF2795F01C91F370AFDDDCDBDBD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6B8CFE32F95EA2497EC2A96EC2A93F02894EF2B96EB2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2894EC2895EE2493EA
+      9EC4E1D8D8D8D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DBDBDB78B5E02393EA2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2497EB2398EA2896EC2A95ED2795EE2295
+      EE2096EF2895EE2994EE2994EE2994EE2995EE2995EE2994EE2994EE2994EE29
+      95EE2995EE2995EE2894EF2093F42294F12796EC2A97E92897E92298E92597EB
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2A95EC2B96EB2895EE2594F02795EF2C96ED2E95EA
+      3D94DEB8CFDED7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B8CFE32F
+      95EA2497EC2A96EC2A93F02894EF2B96EB2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2894EC2794ED409BE7C8D8E1D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7A3C7E22B92E82895EE2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895ED28
+      95ED2995ED2A95ED2995EE2995EE2A95ED2995ED2995EC2995ED2995ED2995EC
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2796EC2696EC2995ED2A94EF2C92F12D91F32C91F32C93EF2C
+      94EF2C93EF2C93EF2C94EF2C94EF2C93EF2C93EF2C93EF2C94EF2C94EF2C93EF
+      2C94EE2D95EB2D95ED2C93F02A93F22993F22793F12794EF2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2A95ED2996EC2795EF2695F12895EE2D93E93594E195C0DFDCDCDCD6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B8CFE32F95EA2497EC2996EC
+      2A93F02894EF2B96EB2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995EC2995ED2995EE2793EC67A9E4DCDDDDD6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6CBD9E1449EE92696EB2C95EC2A95EF2796EB2995ED2A95ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2A95ED2F94ED3094ED2B95EE2396ED
+      2397E91F93E81E92ED2391ED2690F12792EB2194E92491F02791EE2493EB2493
+      EB2493EB2493EB2493EB2493EB2493EB2493EB2493EB2493EB2493EB2493EB26
+      91ED2791EE2493EB2294EA2295E92295E82295E82294E92294E92294E92294E9
+      2294E92294E92294E92294E92294E92294E92294E92294E92295E92296E72295
+      E82293EC2292ED2492EC2793EA2693EA2493EB2493EB2493EB2493EB2493EB24
+      93EB2493EB2493EB2493EB2493EB2493EB2493EB2493EB2493EB2493EB2493EB
+      2493EB2493EB2493EB2493EB2493EB2493EB2493EB2493EB2493EB2593EB2494
+      EA2292EB2692EB3493E35BA2DE9ACAE9DBE2E2D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6B7CFE22D97E92796EC2E95EC2895EE2794F02C95
+      ED2995ED2995ED2995ED2995ED2995ED2A94EF2995EE2796EB2B93F12B94EC28
+      98EB2792EB99C0E5D8D8D8D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DB
+      DBDB71B2E62598E42E96EC2993F22399E82A95ED2B94EE2995ED2995ED2995ED
+      2995ED2995ED2995ED2495EE2995EC2E95EC2395EE1E96EC3A9CE771B7E87DB9
+      E37AB5E88EB7E787B8E07ABADE8AB8E48EB9E28ABADF8ABAE08ABAE08ABAE08A
+      BAE08ABAE08ABAE08ABAE08ABAE08ABAE08ABAE08ABAE08ABAE08ABAE08ABAE0
+      8ABADF8ABAE08ABADF8ABADF8ABAE08ABAE08ABAE08ABAE08ABAE08ABAE08ABA
+      E08ABADF8ABAE08ABAE08ABAE08ABADF8ABAE08ABAE08ABAE08ABAE08ABADF8A
+      BADF8ABADF8ABADF8ABAE08ABAE08ABAE08ABAE08ABAE08ABAE08ABAE08ABAE0
+      8ABAE08ABAE08ABAE08ABAE08ABAE08ABAE08ABAE08ABAE08ABAE08ABAE08ABA
+      DF8ABAE08ABAE08ABAE08ABAE08ABAE08ABAE08BBBDF8BBBE07FB7E487BAE6AD
+      CAE0D6DCE0D9D9D9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6B5D0E12699E62C93EF3394EC2299E92692F32D93EF2995ED2995ED29
+      95ED2995ED2995EC2B92F12A94EF2399E62E8FF72F94EC1F99E63998E8C7D3E1
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7A5C8E12796E3
+      2394F42992F02397EB2A96EB2994EF2995EE2995ED2995ED2995ED2995ED2995
+      ED2196EE2097EC2297EB1E96ED2793ED4A92E7B8CEF0DFDFDFDCDCDCD8D8D8DA
+      DADADBDBDBD8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8
+      D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8
+      D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8
+      D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8
+      D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8
+      D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8
+      D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8D8DBDBDBDBDBDBD7D7D7D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6B7CFE22B
+      97E52A94F12C92F02198EB2894EF2D94EE2995ED2995ED2995ED2995ED2995ED
+      2994F02B94EE2498E92B93F12C93EF1997ED64AAE0DBDCDDD6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D5DBDE49A2E41B92F52B93F12495
+      EF2A97EA2994EF2995EE2995ED2995ED2995ED2995ED2995ED2A94EE3194EC35
+      94EB2F94EE2995EF2996E94B9EDECAD5E1D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6AECAE02E96E52994F12692F3
+      2197EC2895EC2D95EC2995ED2995ED2995ED2995ED2995ED2995ED2B95ED2596
+      EC2896EC2892F41A94EC9AC5DDD8D8D8D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D9D9D986B9E12294EC2B92F02794F02797EA2995EE29
+      95ED2995ED2995ED2995ED2995ED2995ED2695EF2795ED2D96EB2C95ED2794F0
+      2697EC2396E65FA8DED4DCE0D7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D9D9D995BFE22A94E82994EF2593F22596ED2896EC2B95
+      EC2995ED2995ED2995ED2995ED2995ED2996EC2A95ED2694EF2796EB2691F23E
+      9CE5CADADFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6BFD3DE379ADC2B92EF2D93EF2197EC2A95ED2A95ED2995ED2995ED
+      2995ED2995ED2995ED2F93EF2B95ED2098EB1D98ED2195F02894EF2B96EC2094
+      E567AEE0DCE1E0D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6DCDDDD6AADE52492EE2B96EB2994EF2C94EE2796ED2996EC2995ED2995ED29
+      95ED2995ED2995ED2B95EB2995ED2694F12995EE2691EC80B5DCDCDCDCD6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DDDEDD
+      68B2DC2B91EE3292EE1D97EE2C96EB2A95ED2995ED2995ED2995ED2995ED2995
+      ED2A94EF2A95ED2997EA2D94EC2E92F12894F02794EF2C92F32595E482B9DFDF
+      DFDFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D0D8DC3E99E81E
+      92F42C97E72E95EC3193EE2794F02895ED2995ED2995ED2995ED2995ED2995ED
+      2C95EC2895EE2693F22B93F22F96E7BDD0DDD6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7ABCBE02893EB2D94
+      EE2598EA2995EC2995ED2995ED2995ED2995ED2995ED2995ED2695EE2795ED2B
+      95EC2E94ED2E93EF2A94EE2794EF2A93F21D94F02792ECA1C4E1DADADAD6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D9D9D99BC0DF2892EA2594F02B95EC2B95EC
+      2B95ED2994EE2995ED2995ED2995ED2995ED2995ED2995ED2C95EC2996EC2494
+      F02C92EB5DAAE3DBDEDED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7DEE154A3E42893F12D97EA2795EE29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895ED2895ED
+      2995ED2995ED2A95EC2795ED2293F14098E2B6CFE2D9D9D9D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D7D7D7CEDCE2449DE12794ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2C95EC2995ED2995F13292E6ACCBE0D8
+      D8D8D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D8D8D8A6C4DF2E92E92A94F12696EE2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A95ED2D95
+      EC2F97E82B96EA1E90F54298E8C6D9E4D8D8D8D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6E0E1E177B7
+      DC2A91E82894EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2B96EE2995EF2690F161A7E3DBDDDDD6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D9DBDC59A4DF2291F32697EC2996ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2B95ED2A94EF2895EE3098E523
+      94F01C8FF45EA6DFD6DEE1D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D9D9D9A2C6E32F92E53293EC2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2696EC2796F02892EEA5C8DFD7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D8D8D89FC5
+      DF2991E92797EC2995EC2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2693F22294F22F98E73197E42495F02190EE
+      77B0DDDEE1E2D7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6DADADAB6CBDE3C98E41A94F42495EE2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2796EC2693ED54A5
+      E2D6DFE1D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7DEE05BA4DE2693EF28
+      95EE2995EE2A95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2894EF2894F02A95ED2595EF2796EE2C96E82495F22990E87FB7E1E0E4
+      E4D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DFE0E098C2E2
+      3F98DE2594F01C94F22696ED2A95ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2694F13092E7ADCCDFDADADAD6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D9D9D9A7CBE53290E82993F02797EA2597EC
+      2695EF2A96EC2B95EC2A95ED2995ED2995ED2995ED2995ED2995ED2895EF2B95
+      ED3196E92294F31D93F52D96EB2E96EA2095F32F94DF9BC4DCDCDCDCD6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6DADADAC6D4DD6EACE32990ED2297EC2693EF3191
+      F13195EA2995ED2995ED2995ED2995ED2995ED2995ED2B94ED2995ED2B96EB29
+      95EE2695EE2997EB258FF06BACE6DEDFDFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6DAE3E56EAAE2298FEE259AE52197EB2793F12497EB2A95
+      EC2C94EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED28
+      95EE2995ED2995ED2594F12593F33B92E1ACCBE3DCDCDCD7D7D7D7D7D7D7D7D7
+      D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7
+      D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7
+      D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7
+      D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7D7D7D7D7D7D7D7D7D7D7
+      D7D7D7D7D7D7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D7D8D8D8DBDBDBD6DB
+      E0B3CCE27EB1E1409AE52692EC2895EF2895ED2995ED2A95ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2C92F12996EB3097E62E92F12295EF2397EA
+      3B94E6BFD7E7DADADAD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D9D9D9BFD5E33A9BE02993EC3390F12D94ED1C98EC2496EC2B95ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2495EE1F95F42593EE479CDF8BBDE096BFDD90BDE191BEDF91BEDF91BEDF91BE
+      DF91BEDF91BEDF91BEDF91BEDF91BEDF91BEDF91BEDF91BEDF91BEDF91BEDF91
+      BEDF91BEDF91BEDF91BEDF91BEDF91BEDF91BEDF91BEDF91BEDF91BEDF91BEDF
+      91BEDF91BEDF91BEDF91BEDF91BEDF91BEDF91BEDF91BEDF91BEDF91BEDF91BE
+      DF91BEDF91BEDF92BFDF94BEDE97BDDFC8D3DCD6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6C9D5D994BFDB92BEDB94BFDE94BDE094BDE093BFDD9DBEDB
+      CCD5DFD7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6C4D1DB95BDDE93BD
+      E092BDE094BEDF97BEDE90BEE18EBDDF8CBCE278B2E054A3E02C96E61B94ED23
+      95EF2A95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2193F32997EA2E95EC3591F02893F11E91EA79B8E3E2E6E6D7D7
+      D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DBDBDB88
+      C0E02A8FEA378FF42797EA2196EE2895EE2795EC2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2C96EA2A95EC2495
+      F12795ED2993E72592EE1D90F42692EA2792E92792E92792E92792E92792E927
+      92E92792E92792E92792E92792E92792E92792E92792E92792E92792E92792E9
+      2792E92792E92792E92792E92792E92792E92792E92792E92792E92792E92792
+      E92792E92792E92792E92792E92792E92792E92792E92792E92792E92792E927
+      93E72795E62690EB9BBFDDD7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      BAD3DC2D95E62991ED2591EC2D92E72993E62594E63291E3A4C4E1D8D8D8D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6ABCADE2F91E42692EB2593EA2592E92B
+      92E72492ED2595EA2492EA2693EE2593EE2796EE2897EA2996EC2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2594
+      F22695EE2895ED2F95ED2592F2479DE7CEDFE5D7D7D7D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DEE25BA5E21F95EA
+      2998E92B93EF2A94EF2A95EC2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2694F22492F52493F32794EF2A95EC29
+      96ED2795EF2696ED2696ED2695ED2695ED2696ED2696ED2695ED2695ED2696ED
+      2696ED2696ED2695ED2695ED2696ED2696ED2695ED2695ED2696ED2696ED2695
+      ED2695ED2695ED2696ED2696ED2695ED2695ED2696ED2696ED2695ED2695ED26
+      96ED2696ED2696ED2695ED2695ED2696ED2696ED2695ED2896EC2795ED1D90F3
+      77AFDEDCDCDCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6CEDADD4C9FEB2D8F
+      F72992F32F96E82598E91F94EF2291F284B6E0DADADAD6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6C6D8E03F99E52292EE2797EC2295EF2895EE2894ED2C98E7
+      2A96EB2894F02992F22D93F03193EE2B94EC2895ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED3394EC2592F62397EC20
+      9AE82C92E5ABC9E4DADADAD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7BAD2E12C9DDB2F96E92E92ED2695
+      EF2C95EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2D95EB3096E92F96EA2C96EC2795EE2695EF2696EE2895ED
+      2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895
+      ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED28
+      95ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED
+      2895ED2895ED2895ED2895ED2895ED2996ED2296EC1895F04AA1E2D4DCE1D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DCDCDC6BAEE12394ED2C93EE3196E822
+      97EB2893F11F92F65CA8E3DBDDDDD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      DBDEDE5CA5E32692EE2B97E92395F02A95EE2E92F02D90F62A91F52793F22595
+      F02496ED2497EB2896EC2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED3195EA2992F42396EE2297E076B2DDDFDFDF
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6DCDCDC8BBDE02393E62F95EE2894EF2195EF2A95ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2795ED2996EC2B96EB2A95ED2794F02A94EE2F96EB2C94ED2B94EE2B94EE2B94
+      EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B
+      94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE
+      2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94EE2B94
+      EE2B94EE2B94EE2D94ED3094EB2895E93799E7BFD2E0D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D8D8D896BDDD2794E82A93EF2896ED1C96F03493EC2495F0
+      3CA0E1CED8DDD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DBDBDB76B3E72492
+      ED2A97E72195F02894EF2C94ED2695EE2896EC2B96EA2C95EB2A94EF2893F328
+      95EF2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED1F98EB2E98EB2691F0479EE2D1DDE2D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6DAE0E357ACE1278FEF2F94EE2695ED2993F02B95EE2894EF2694F0
+      2996EC2C96EA2A95ED2995ED2995ED2995ED2995ED2995ED2695ED2496ED2695
+      ED2595ED2495EF2695EE2895ED2895ED2895ED2895ED2895ED2895ED2895ED28
+      95ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED
+      2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895
+      ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2895ED2A
+      95ED3092F11F97F02796E6ABC4E0D7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6AFCCDF2795E62495F02795ED2496ED3195EC2B96EC2994E6A8CCE5D7D7
+      D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D8D8D89FC4E03194E72495F02794F029
+      95ED2796ED2597EB2697EA2897EB2996EB2795ED2695EF2895EE2995ED2995ED
+      2995ED2995ED2995ED2A96EB2A97EA2895ED2894EE2B95ED2B94EF2593F22096
+      EC2D94EF3791E8ABCDE7D9D9D9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7
+      C7D6E1459CE12295EE2A94ED2C95ED2D95EB2A95ED2595EF2695EE2A96EB2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2A95ED2995ED2995ED2A95ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2B94ED2F94ED1D99EF
+      1C98E65DA5DDD2DDE5D9D9D9D7D7D7D7D7D7D7D7D7D7D7D7DEDEDEA1C5E32A8F
+      ED2294F12895EC2A95ED2895ED2B95ED2592ED61AAE3DDE2E2D8D8D8D7D7D7D7
+      D7D7D7D7D7D7D7D7E2E2E28BBEE12D91E62193F52B94EE2A95ED2595EF2A95ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2996EC2796EC2795EE2A95EC2F95EA2E95EB2894F02193F62594E790BEE0DE
+      DEDED6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D8D8D8B0CCDF2896
+      E11F95EF2C96EC2B96EB2B96EA2996EC2695EF2594F02995EE2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2B95EC2B95EE2496EF2296E259AA
+      DEB0CBE5C8DAE8C2DDE9C4DBE9C3DDEA9FD0E9459EDD1F93F12494F02995EC29
+      95ED2995ED2994EC2A94EE2C95E063B0D9AFD1E8C6DAEBC4D8E8C3DBE9C3DDE8
+      9DCAE9439ADF2595EE2394F02D95EB2B95ED2595EF2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2695EF2394F128
+      95ED2C96EA2D96E92C96EA2995ED1B8FF36FB1DEDDDFDED6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DCDCDC93BFDE2895E72394F31F
+      93F22495EF2B96EA2A95ED2894F02995EE2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2895ED2896EB3293EE3591F12A94F12295E82D97E12E9AE22A
+      9AE83996E83A98E62A96E02697E62D94EE2B94EE2A95ED2995ED2995ED2894EC
+      2A95EC2593F22390F33293E93897E63198E72A9AE8299BE32F94E22894EB1F96
+      F02695EE2F95EB2C95EC2695EE2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2795EF2794EF2B96EB2996EC2395F0
+      2295F22493ED55A2E2D2D9DFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6DDDDDD82B4DF2B91E92093F61E93F32A96EB
+      2D95EB2B95EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED28
+      95ED2496EC2396ED2595F02A92F22992F21F94F21D95F12A92F23091F32D92F1
+      2595EF2796ED3094EC2D95ED2B95ED2A95ED2995ED2995ED2995ED2A94EF2C93
+      EF2797EA2396EB2794F12F90F33690F12992F22095F12096EF2895ED2E94EC2B
+      95ED2695ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2B95EC2C96EA2396EE1E94F62594EF459ADFC3D3
+      E1D7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6DEE26BA9DC2E91E92194F32996EB2D95EC2A93F02A95
+      ED2995ED2895ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A95ED2B95ED2396ED
+      1E97ED2596ED2D93EF2D92F22A94EE2497E72098E72097EA2496EE2495EE2496
+      EC2A96EC2C95ED2A95ED2995ED2995ED2795ED2894EE2995EE2498EA2497EB29
+      93F02C92F12B93F02893F02696EE2796EC2994EE2A93EF2995EE2795EC2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2895EE2995EE2A96EB2295EF2293F14096DDAFCDE1D9D9D9D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D9D9D9CEDDE35EA4DC2791EE2994F12C96EB2795ED2595EF2995EE2C94EE2C
+      94EE2995EE2896EC2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2A95ED3093EF3193ED2E95EB2996EB2696
+      EB2695ED2C93F02C93F02596EC2795ED3092EF2F93EE2A96EB2A95ED2B95ED2B
+      95ED2A95ED2895ED2795ED2794F02594F12795ED2E95EC2F92EE2894ED1F97EA
+      2895EC2C95EB2B96EB2994EE2893F02795EE2896EC2995ED2995ED2995ED2995
+      ED2995ED2995ED2A96EC2995ED2795EE2895EF2995ED2995EE2795F02695ED29
+      95EC2293F13395E3A3C6E1DCDCDCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7C8
+      D9E157A0E3228FF62796EC299BE42095F02B94EE3293EE2E92F02994EF2898E9
+      2996EB2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2A95ED2A95ED2995ED2895ED2895ED2995EE2A
+      95EE2995ED2995ED2A94EE2A95ED2A95ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2895ED2995ED2A95ED2A95ED2995ED2995ED2995ED2A95ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A
+      97EA2895ED2494F12794F02C95EC2B94EE2994ED2996EC2394F42E92E89BC2DF
+      DCDCDCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7C9D7DF469FE1
+      1D91F42994EF2F94EB2D96EA2797EA2397EC2795EE2D94EE2C95EE2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2795EF2794F02895EE
+      2B96EC2D96E92C96EA2B96EB2093F42391ED97BEDFDCDCDCD6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D8D8D8C2D6E04EA0E2278FF62B93
+      EF2595EE2196ED2497EB2996EC2C93F02A94EF2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2793F12A94F02C95EC2A96EB2696EE2596
+      EC2495EF2A92E994BDDFDCDCDCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7C9D5DF4E9FE12193EC2093F52592F52F
+      94EE3096EA2696EE2695EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2B94EE2E94EB2C96EB2395EF1F94F42393EE3295E298C1DFDC
+      DCDCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D8D8D8C2D8E253A4DC2E92E82A91F63192F03195EA2696ED
+      2595EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A
+      95ED2E96EA2B96EB2194F22792EF4395DEA1C4DFDCDCDCD6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D9D9D9D0DFE56AAADD2791E92494EF2C96EB2B93EF2894EF2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2595F02996EC2A96EC
+      2592EC479AE0ADC8DFDCDCDCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D8D8
+      D8D8DFE37FB8DE2297E12095EF2E93EF2095F12496EE3394EC2A95EE2297EC2A
+      96EC3093F02A95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895ED2494F1
+      1E94F32693F13594EB3495EA2797EC2097EC2294F22290F054A1E2C0D2E0D9D9
+      D9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DDDDDD9E
+      C4E0319BE02593EC3594ED2A93F02693F22A96EC2495EE2095EF2897E92995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2B95ED2C95ED2597EA2595EE2991
+      F52C92F32A97E92495ED2692E974AFDED0D9E0D7D7D7D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DADADABACEDE4FA2E2
+      2892EB2D94ED2995EB2695EF2996EC2797EA2197ED2895ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2A97EA3197E63295EB2A95ED1F97ED1D97EF2891F137
+      91EA8EBBDDDDDEDCD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7CBD9E16CB3E42995E12499
+      E62896EC3593EE3092EF2394F22895EE2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2795EE2694F02A93EF2996EC2598E72D94E1539FE5ABC9E4DDDDDDD6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DEDFDF9AC5E1409DDF2991EA2A90F727
+      93F02899E52996EC2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A95ED2496EE2495F0
+      2D91F02791F12893E571B1DBCED8DED8D8D8D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6DADADACAD4E06CACE52795E32698E52A96EB2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895ED
+      2995ED2995ED2995ED2995ED2995ED2895ED2D95EA2D95EB2391EB479BE4A5C6
+      E2DDDFDFD6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6DDE1E19DC8E0439AE32990ED2893F12895EE2896EB2796
+      EC2895EE2995EE2A94ED2B95ED2994EE2995EE2995EE2B95ED2B95EC2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2895ED2795EE2895EE2994EE2A94EE2D95ED2E94ED2996EB2795ED2995
+      ED2A96EB2996EC2793F02490F03092E577B4DECAD9E0D9D9D9D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6DBDBDBC9DCE77DB7E03B97E42991EB2594F12696EE2796EA2896ED29
+      94F02A94EF2B94EE2A95EC2A96EB2995EC2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895EF2694
+      F12995EF2C95EC2B95EC2A96EC2A95EE2997EC2896EC2795EE2596EF2795EC2E
+      90E858A4E4ADD1E4E1E1E1D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D8
+      D8D8DFDFDFBFD5DF77B0E03395EC2592F02B95EC2B94E82896ED2796EC2897EA
+      2898E82697EA2496ED2395F02895EE2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2A95ED2B96EC2B96EA2B97E925
+      97E91E97ED1A97F02694F12E92F32990F42490ED4EA4E29EC6DDD7E2E2DDDDDD
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      DDDDDDB9D0DF79B3E03A97E52490EF2A94F02C94ED2A95EE2895EE2A93EF2E92
+      F02E93F02A95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2996EC2897EA2896EC2894EE2993F02C93F22C92F1
+      2694EE2891EA51A1E59AC3E2D3DDE0D9D9D9D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6DBDB
+      DBBCD1DF6EB0E53998E9298FEB2A94EE2894EF2893EF2B93F22C93F22A95EE28
+      94EC2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995EC2995F02792F22993F12A95F02C95ED2A92E92F94E650A6E294C3E1D4DE
+      E1D9D9D9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7DCDCDCC3
+      D4E080B9DF4BA0E12595ED1A93EF2698E9309AE12A96EC2895ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895ED2A98E92598
+      E41D95EF1D92EE3998E46CADDBABCBDDD7DDDFDADADAD6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7DEDEDECFDBDF
+      A8C8DF6CAFDF3A99E22491E82693ED2895EE2895EE2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2895EE2795EE2592E92F96E45AA6DE92BDDFC1
+      D3E0DEE0E0D8D8D8D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7D7DEDFDEBFD3
+      E398C1E762ABE53F9AE52A92E92391EF2493F02694EE2C96EC2D95E92C95EA2B
+      96EC2896EC2695ED2595EE2695EF2695EE2795EC2895EC2A96EC2B96EB2A95ED
+      2894F02894F02894F02894F02894F02894EF2796EC2896EC2896EC2A95ED2B95
+      EE2C94EE2995ED2598E92797EA2996EB2B95EC2D95ED2E95ED2A95EE2394EF1E
+      93EF2292EC3495E65AA4E486B6E5B5CCE5D7DCDFD8D8D8D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D8D8D8DBDDDDC7
+      D5DEA1C6E171B1E54A9DE53496E82991EC2790F12791F12893F12A94EF2A95ED
+      2A95EC2A93EE2A93EF2A94F02993F02893F12793F22995EF2B96EB2B95EB2B96
+      EB2B96EB2B96EB2B95EC2D94ED2D94EF2B94EF2B93F02893F22793F32893F129
+      93EF2A94F02893EE2793EE2793EE2792EC2F93E83F9AE664AAE590BDE0B9CFE0
+      D7DBDEDADADAD5D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D9D9D9DEDEDE
+      D2DDE2BFD3DF9DC4E178B5E862A9E2499FE23497E42994E62694EA2693EB2694
+      EB2595EB2695EB2597EA2597E92697EA2796EC2796EC2796EC2796EC2796EC28
+      96EC2D93F32C92F12893ED2694E92497E7239AE52497E82793EC2D94E83E9AE3
+      55A6E16CB0E085BFE4B2CEDECBD7E0DDDDDED9D9D9D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D7D7
+      D7DADADADCDDDCCDD8DDB2D0E29BC4E58CBBE375B3E75FA9E555A4E44AA1E439
+      98E23697E13194E72D92ED2C92EC2C92EC2C92EC2D92ED2E93ED2E96E93498E6
+      469EE454A3E261A8E274B0E184BAE093C2E1A8CDE5C2D6E0D6DCDEDCDCDCD9D9
+      D9D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D7D7D7D8D8D8D8D8D8D9D9D9DDDCDBDADCDDD0DAE1BED2E5B5CEE6B3CCE4
+      B3CCE2B2CBE1B1CAE1B1CAE1B3CBE2B4CCE2B3CFE2B9D2E2CAD7DFD6DADEDCDB
+      DCDADADAD8D8D8D8D8D8D7D7D7D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6D6
+      D6D6}
+    Stretch = True
+    Transparent = True
+  end
+  object lblTitle: TLabel
+    Left = 8
+    Top = 8
+    Width = 590
+    Height = 41
+    Alignment = taCenter
+    AutoSize = False
+    Caption = 'Ask for a PASA (Account)'
+    Color = clBtnFace
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clWindowText
+    Font.Height = -32
+    Font.Name = 'Tahoma'
+    Font.Style = [fsBold]
+    ParentColor = False
+    ParentFont = False
+    Transparent = False
+  end
+  object pnlBottom: TPanel
+    Left = 0
+    Top = 337
+    Width = 615
+    Height = 51
+    Align = alBottom
+    BevelOuter = bvNone
+    TabOrder = 0
+    DesignSize = (
+      615
+      51)
+    object bbExecute: TBitBtn
+      Left = 330
+      Top = 10
+      Width = 130
+      Height = 31
+      Action = actExecute
+      Anchors = [akTop, akRight]
+      Caption = 'Ask for a PASA'
+      Glyph.Data = {
+        DE010000424DDE01000000000000760000002800000024000000120000000100
+        0400000000006801000000000000000000001000000000000000000000000000
+        80000080000000808000800000008000800080800000C0C0C000808080000000
+        FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333333333333
+        3333333333333333333333330000333333333333333333333333F33333333333
+        00003333344333333333333333388F3333333333000033334224333333333333
+        338338F3333333330000333422224333333333333833338F3333333300003342
+        222224333333333383333338F3333333000034222A22224333333338F338F333
+        8F33333300003222A3A2224333333338F3838F338F33333300003A2A333A2224
+        33333338F83338F338F33333000033A33333A222433333338333338F338F3333
+        0000333333333A222433333333333338F338F33300003333333333A222433333
+        333333338F338F33000033333333333A222433333333333338F338F300003333
+        33333333A222433333333333338F338F00003333333333333A22433333333333
+        3338F38F000033333333333333A223333333333333338F830000333333333333
+        333A333333333333333338330000333333333333333333333333333333333333
+        0000}
+      NumGlyphs = 2
+      TabOrder = 0
+    end
+    object bbCancel: TBitBtn
+      Left = 482
+      Top = 10
+      Width = 116
+      Height = 31
+      Anchors = [akTop, akRight]
+      Kind = bkCancel
+      NumGlyphs = 2
+      TabOrder = 1
+    end
+  end
+  object reDescription: TMemo
+    Left = 214
+    Top = 64
+    Width = 384
+    Height = 218
+    Anchors = [akLeft, akTop, akRight]
+    BorderStyle = bsNone
+    Font.Charset = ANSI_CHARSET
+    Font.Color = clWindowText
+    Font.Height = -13
+    Font.Name = 'Verdana'
+    Font.Style = []
+    Lines.Strings = (
+      'Pascalcoin works like a Bank'
+      ''
+      'For this reason, you need an Account to store your '
+      'Pascals.'
+      ''
+      'Accounts are also called PASA (PAScal Account) and '
+      'are '
+      'a short sequential number with a checksum value like '
+      '"1234-54" where "54" is the checksum'
+      ''
+      'With this assistant you will be able to ask for a Free '
+      'Account to the network')
+    ParentColor = True
+    ParentFont = False
+    ReadOnly = True
+    ScrollBars = ssVertical
+    TabOrder = 1
+  end
+  object ebMessageToSend: TEdit
+    Left = 17
+    Top = 307
+    Width = 581
+    Height = 21
+    Anchors = [akLeft, akTop, akRight]
+    TabOrder = 2
+  end
+  object ActionList: TActionList
+    Left = 156
+    Top = 78
+    object actExecute: TAction
+      Caption = 'Execute (F12)'
+      ShortCut = 123
+      OnExecute = actExecuteExecute
+    end
+  end
+end

+ 161 - 0
src/gui-classic/UFRMAskForAccount.pas

@@ -0,0 +1,161 @@
+unit UFRMAskForAccount;
+
+{ Copyright (c) 2021 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+interface
+
+{$I ../config.inc}
+
+uses
+{$IFnDEF FPC}
+  pngimage, Windows, Messages,
+{$ELSE}
+  LCLIntf, LCLType, LMessages,
+{$ENDIF}
+  SysUtils, Variants, Classes, Graphics, Controls, Forms,
+  Dialogs, StdCtrls, UAccounts, Buttons, ActnList,
+  ExtCtrls, ComCtrls,
+  {$IFNDEF FPC}System.Actions, System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
+  UNode, UWallet, UNetProtocol, UPCDataTypes, UThread, UBaseTypes,
+  UBlockChain;
+
+type
+
+  { TFRMAskForAccount }
+
+  TFRMAskForAccount = class(TForm)
+    ActionList: TActionList;
+    actExecute: TAction;
+    pnlBottom: TPanel;
+    bbExecute: TBitBtn;
+    bbCancel: TBitBtn;
+    reDescription: TMemo;
+    lblDoYouWantToSendAMessage: TLabel;
+    ebMessageToSend: TEdit;
+    imgAskForAccount: TImage;
+    lblTitle: TLabel;
+    procedure actExecuteExecute(Sender: TObject);
+    procedure FormCreate(Sender: TObject);
+  private
+    { Private declarations }
+    FNode : TNode;
+    FNetData : TNetData;
+    FWalletKeys : TWalletKeysExt;
+    FNewPublicKey : TAccountKey;
+  public
+    { Public declarations }
+    class function AskForAccount(AOwnerForm : TComponent; ANode : TNode; ANetData : TNetData; AWalletKeys : TWalletKeysExt; const ANewPubliKey : TAccountKey) : Boolean;
+  end;
+
+
+implementation
+
+{$R *.dfm}
+
+uses UFRMSplash, UPCTNetDataExtraMessages,
+  UPCAbstractMemAccountKeys,
+  UOrderedList;
+
+procedure TFRMAskForAccount.actExecuteExecute(Sender: TObject);
+var Ltc, Ltc2 : TTickCount;
+  Lnodes, LtotalNodes : Integer;
+  Lmempool : TPCOperationsComp;
+  LOnsafebox,LOnMempool : Integer;
+begin
+  // Ask
+  LOnSafebox := 0;
+  LOnMempool := 0;
+  LtotalNodes := 0;
+
+  if (ebMessageToSend.Text='') then begin
+    if (Application.MessageBox(PChar('Are you sure to send an EMPTY message to the blockchain?'),
+      PChar(Application.Title),MB_ICONQUESTION+MB_YESNO+MB_DEFBUTTON2)<>IdYes) then Exit;
+  end;
+
+  Try
+    TFRMSplash.SplashStart(Self,'Ask for PASA',True,True,100,0);
+    try
+      TFRMSplash.SplashUpdate('Checking current state... '+ebMessageToSend.Text,10);
+      FNode.GetAccountsAvailableByPublicKey(FNewPublicKey,LOnsafebox,LOnMempool);
+      if (LOnMempool>0) then raise Exception.Create(Format('%d accounts are on the mempool and will be available after next block.'+#10+
+         'Your wallet will have at least %d accounts, no need to ask for more!',[LOnMempool,LOnsafebox+LOnMempool]));
+      if (LOnSafebox>0) or (LOnMempool>0) then raise Exception.Create(Format('Your wallet has at least %d accounts, no need to ask for more!',[LOnsafebox+LOnMempool]));
+      TFRMSplash.SplashUpdate('Sending message to nodes... '+ebMessageToSend.Text,20);
+      Ltc := TPlatform.GetTickCount;
+      repeat
+        TFRMSplash.SplashUpdate(Format('Asking to %d nodes',[FNetData.ConnectionsCountAll]),10);
+        Lnodes := TPCTNetDataExtraMessages.AskForFreeAccount(FNewPublicKey,ebMessageToSend.Text);
+        inc(LtotalNodes,Lnodes);
+        //
+        if (LNodes>0) then begin
+          Ltc2 := TPlatform.GetTickCount;
+          repeat
+            TFRMSplash.SplashUpdate(Format('Asked to %d nodes (total %d) waiting for success...',[Lnodes,LtotalNodes]),40);
+            sleep(100);
+            FNode.GetAccountsAvailableByPublicKey(FNewPublicKey,LOnsafebox,LOnMempool);
+          until (Application.Terminated) or (Self.ModalResult<>0) or (LOnsafebox>0) or (LOnMempool>0) or (TPlatform.GetElapsedMilliseconds(Ltc2)>(10000));
+        end else sleep(500);
+      until (Application.Terminated) or (Self.ModalResult<>0) or (LOnsafebox>0) or (LOnMempool>0)
+        or (FNetData.ConnectionsCountAll<=0)
+        or (TPlatform.GetElapsedMilliseconds(Ltc)>(60 * 60 * 1000)); // 1 hour
+    finally
+      TFRMSplash.SplashFinalize;
+    end;
+    if (LOnsafebox+LOnMempool)=0 then begin
+      Application.MessageBox(PChar('No accounts have been detected...'),PChar(Application.Title),MB_ICONERROR+MB_OK);
+      ModalResult := MrNone;
+    end else begin
+      Application.MessageBox(PChar(Format('Congratulations!'+#10+#10+'%d new accounts are on the way!'+#10+#10+'Total accounts will be %d',[LOnMempool,LOnsafebox+LOnMempool])),PChar(Application.Title),MB_ICONINFORMATION+MB_OK);
+      ModalResult := MrOk;
+    end;
+  Except
+    on E:Exception do begin
+      if (E is ESplashCancel) and ((LOnsafebox+LOnMempool)>0) then begin
+        ModalResult := MrOk;
+      end else Raise;
+    end;
+  End;
+end;
+
+class function TFRMAskForAccount.AskForAccount(AOwnerForm: TComponent;
+  ANode: TNode; ANetData: TNetData; AWalletKeys: TWalletKeysExt; const ANewPubliKey : TAccountKey): Boolean;
+var FRM : TFRMAskForAccount;
+begin
+  TPCTNetDataExtraMessages.InitNetDataExtraMessages(ANode,ANetData,AWalletKeys);
+  FRM := TFRMAskForAccount.Create(AOwnerForm);
+  Try
+    FRM.FNode := ANode;
+    FRM.FNetData := ANetData;
+    FRM.FWalletKeys := AWalletKeys;
+    FRM.FNewPublicKey := ANewPubliKey;
+    Result := FRM.ShowModal=MrOk;
+  Finally
+    FRM.Free;
+  End;
+end;
+
+procedure TFRMAskForAccount.FormCreate(Sender: TObject);
+begin
+  bbCancel.Cancel := True;
+  bbCancel.ModalResult := MrCancel;
+end;
+
+end.

+ 38 - 16
src/gui-classic/UFRMOperation.dfm

@@ -90,7 +90,7 @@ object FRMOperation: TFRMOperation
   object bbCancel: TBitBtn
     Left = 461
     Top = 476
-    Width = 116
+    Width = 120
     Height = 31
     Kind = bkCancel
     NumGlyphs = 2
@@ -185,7 +185,7 @@ object FRMOperation: TFRMOperation
         object lblEncryptionErrors: TLabel
           Left = 255
           Top = 96
-          Width = 187
+          Width = 152
           Height = 33
           AutoSize = False
           Caption = 'Errors detected'
@@ -211,7 +211,7 @@ object FRMOperation: TFRMOperation
         object rbEncryptedWithEC: TRadioButton
           Left = 15
           Top = 35
-          Width = 207
+          Width = 234
           Height = 19
           Caption = 'Encrypted with dest account public key'
           Checked = True
@@ -222,7 +222,7 @@ object FRMOperation: TFRMOperation
         object rbEncrptedWithPassword: TRadioButton
           Left = 15
           Top = 53
-          Width = 141
+          Width = 234
           Height = 19
           Caption = 'Encrypted with password'
           TabOrder = 2
@@ -232,7 +232,7 @@ object FRMOperation: TFRMOperation
         object rbNotEncrypted: TRadioButton
           Left = 15
           Top = 93
-          Width = 162
+          Width = 234
           Height = 19
           Caption = 'Dont encrypt (Public payload)'
           TabOrder = 4
@@ -266,21 +266,25 @@ object FRMOperation: TFRMOperation
         end
         object rbEncryptedWithOldEC: TRadioButton
           Left = 15
-          Top = 16
-          Width = 159
+          Top = 12
+          Width = 234
           Height = 19
           Caption = 'Encrypted with old public key'
           TabOrder = 0
           TabStop = True
           OnClick = memoPayloadClick
         end
-        object cbPayloadAsHex: TCheckBox
-          Left = 448
+        object cbPayloadDataInputType: TComboBox
+          Left = 413
           Top = 96
-          Width = 97
-          Height = 17
-          Caption = 'As Hex'
+          Width = 89
+          Height = 21
+          Style = csDropDownList
           TabOrder = 6
+          Items.Strings = (
+            'As String'#11
+            'As Hexadecimal'#11
+            'As Base58')
         end
       end
       object ebFee: TEdit
@@ -295,11 +299,14 @@ object FRMOperation: TFRMOperation
         Top = 7
         Width = 524
         Height = 204
-        ActivePage = tsBuyAccount
+        ActivePage = tsTransaction
         TabOrder = 0
         OnChange = PageControlOpTypeChange
         object tsTransaction: TTabSheet
           Caption = 'Transaction'
+          DesignSize = (
+            516
+            176)
           object lblDestAccount: TLabel
             Left = 13
             Top = 32
@@ -335,10 +342,11 @@ object FRMOperation: TFRMOperation
             ParentFont = False
           end
           object sbSearchDestinationAccount: TSpeedButton
-            Left = 208
+            Left = 490
             Top = 29
             Width = 23
             Height = 22
+            Anchors = [akTop, akRight]
             Glyph.Data = {
               36030000424D3603000000000000360000002800000010000000100000000100
               18000000000000030000120B0000120B00000000000000000000FF00FF4A667C
@@ -371,17 +379,31 @@ object FRMOperation: TFRMOperation
           object ebDestAccount: TEdit
             Left = 115
             Top = 29
-            Width = 87
+            Width = 369
             Height = 21
+            Anchors = [akLeft, akTop, akRight]
             TabOrder = 0
           end
           object ebAmount: TEdit
             Left = 115
-            Top = 58
+            Top = 56
             Width = 87
             Height = 21
             TabOrder = 1
           end
+          object memoEPASA: TMemo
+            Left = 13
+            Top = 96
+            Width = 492
+            Height = 65
+            TabStop = False
+            BorderStyle = bsNone
+            ReadOnly = True
+            TabOrder = 2
+            WantReturns = False
+            OnChange = memoPayloadClick
+            OnClick = memoPayloadClick
+          end
         end
         object tsChangePrivateKey: TTabSheet
           Caption = 'Change Key'

+ 39 - 16
src/gui-classic/UFRMOperation.lfm

@@ -15,7 +15,7 @@ object FRMOperation: TFRMOperation
   OnCreate = FormCreate
   OnDestroy = FormDestroy
   Position = poOwnerFormCenter
-  LCLVersion = '2.0.2.0'
+  LCLVersion = '2.0.10.0'
   object lblAccountCaption: TLabel
     Left = 25
     Height = 13
@@ -142,7 +142,7 @@ object FRMOperation: TFRMOperation
           Left = 255
           Height = 33
           Top = 87
-          Width = 182
+          Width = 153
           AutoSize = False
           Caption = 'Errors detected'
           Font.Color = clRed
@@ -223,12 +223,18 @@ object FRMOperation: TFRMOperation
           OnClick = memoPayloadClick
           TabOrder = 0
         end
-        object cbPayloadAsHex: TCheckBox
-          Left = 444
-          Height = 19
-          Top = 92
-          Width = 54
-          Caption = 'As Hex'
+        object cbPayloadDataInputType: TComboBox
+          Left = 412
+          Height = 21
+          Top = 88
+          Width = 89
+          ItemHeight = 13
+          Items.Strings = (
+            'As String'#11
+            'As Hexadecimal'#11
+            'As Base58'
+          )
+          Style = csDropDownList
           TabOrder = 6
         end
       end
@@ -244,8 +250,8 @@ object FRMOperation: TFRMOperation
         Height = 167
         Top = 11
         Width = 521
-        ActivePage = tsListAccount
-        TabIndex = 2
+        ActivePage = tsTransaction
+        TabIndex = 0
         TabOrder = 0
         OnChange = PageControlOpTypeChange
         object tsTransaction: TTabSheet
@@ -285,7 +291,7 @@ object FRMOperation: TFRMOperation
             Left = 115
             Height = 21
             Top = 29
-            Width = 85
+            Width = 357
             TabOrder = 0
           end
           object ebAmount: TEdit
@@ -296,9 +302,9 @@ object FRMOperation: TFRMOperation
             TabOrder = 1
           end
           object sbSearchDestinationAccount: TSpeedButton
-            Left = 208
+            Left = 480
             Height = 22
-            Top = 29
+            Top = 28
             Width = 23
             Glyph.Data = {
               36030000424D3803000000000000360000002800000010000000100000000100
@@ -330,6 +336,23 @@ object FRMOperation: TFRMOperation
             }
             OnClick = sbSearchDestinationAccountClick
           end
+          object memoEPASA: TMemo
+            Left = 13
+            Height = 48
+            Top = 88
+            Width = 492
+            BorderStyle = bsNone
+            Font.Color = clBlack
+            Font.Height = -16
+            Font.Name = 'Tahoma'
+            OnChange = memoPayloadClick
+            OnClick = memoPayloadClick
+            ParentFont = False
+            ReadOnly = True
+            TabOrder = 2
+            TabStop = False
+            WantReturns = False
+          end
         end
         object tsChangePrivateKey: TTabSheet
           Caption = 'Change key'
@@ -1024,7 +1047,7 @@ object FRMOperation: TFRMOperation
     end
     object tsGlobalError: TTabSheet
       Caption = 'Notification'
-      ClientHeight = 325
+      ClientHeight = 357
       ClientWidth = 548
       ImageIndex = 1
       TabVisible = False
@@ -1148,8 +1171,8 @@ object FRMOperation: TFRMOperation
     Text = 'ebSenderAccount'
   end
   object ActionList: TActionList
-    left = 140
-    top = 350
+    Left = 140
+    Top = 350
     object actExecute: TAction
       Caption = 'Execute (F12)'
       OnExecute = actExecuteExecute

+ 383 - 241
src/gui-classic/UFRMOperation.pas

@@ -32,7 +32,7 @@ uses
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs, StdCtrls, UNode, UWallet, UCrypto, Buttons, UBlockChain,
   UAccounts, UFRMAccountSelect, ActnList, ComCtrls, Types, UFRMMemoText,
-  UPCEncryption, UBaseTypes, UPCDataTypes, UPCOrderedLists;
+  UPCEncryption, UBaseTypes, UPCDataTypes, UPCOrderedLists, UEPasa, UEncoding;
 
 Const
   CM_PC_WalletKeysChanged = WM_USER + 1;
@@ -129,9 +129,10 @@ type
     ebHashLock: TEdit;
     btnHashLock: TSpeedButton;
     sbTimeLock: TSpeedButton;
-    cbPayloadAsHex: TCheckBox;
     lblChangeAccountData: TLabel;
     ebChangeAccountData: TEdit;
+    cbPayloadDataInputType: TComboBox;
+    memoEPASA: TMemo;
     procedure ebNewPublicKeyExit(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
@@ -159,6 +160,7 @@ type
     FDefaultFee: Int64;
     FEncodedPayload : TOperationPayload;
     FDisabled : Boolean;
+    FUpdating : Boolean;
     FSenderAccounts: TOrderedCardinalList; // TODO: TOrderedCardinalList should be replaced with a "TCardinalList" since signer account should be processed last
     procedure SetWalletKeys(const Value: TWalletKeys);
     Procedure UpdateWalletKeys;
@@ -166,8 +168,8 @@ type
     Procedure UpdateAccountsInfo;
     Function UpdateFee(var Fee : Int64; errors : String) : Boolean;
     Function UpdateOperationOptions(var errors : String) : Boolean;
-    Function UpdatePayload(Const SenderAccount : TAccount; var errors : String) : Boolean;
-    Function UpdateOpTransaction(Const SenderAccount : TAccount; var DestAccount : TAccount; var amount : Int64; var errors : String) : Boolean;
+    Function UpdatePayload(Const ASenderAccount : TAccount; var AErrors : String) : Boolean;
+    Function UpdateOpTransaction(const ASenderAccount: TAccount; out ATargetEPASA : TEPasa;  out ATargetAccount : TAccount; out AResolvedTargetKey : TAccountKey; out ATargetRequiresPurchase : Boolean; out AAmount: Int64; out AErrors: String) : Boolean;
     Function UpdateOpChangeKey(Const TargetAccount : TAccount; var SignerAccount : TAccount; var NewPublicKey : TAccountKey; var errors : String) : Boolean;
     Function UpdateOpListAccount(Const TargetAccount : TAccount; var SalePrice : Int64; var SellerAccount,SignerAccount : TAccount; var NewOwnerPublicKey : TAccountKey; var LockedUntilBlock : Cardinal; var HashLock : T32Bytes; var errors : String) : Boolean;
     Function UpdateOpDelist(Const TargetAccount : TAccount; var SignerAccount : TAccount; var errors : String) : Boolean;
@@ -179,6 +181,7 @@ type
     procedure CM_WalletChanged(var Msg: TMessage); message CM_PC_WalletKeysChanged;
     Function GetDefaultSenderAccount : TAccount;
     procedure ebAccountKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
+    function CaptureEPasa(const AEPasaTxt : String; out AEPasa : TEPasa) : Boolean;
   protected
     procedure searchAccount(editBox : TCustomEdit);
   public
@@ -203,215 +206,224 @@ uses
 { TFRMOperation }
 
 procedure TFRMOperation.actExecuteExecute(Sender: TObject);
-Var errors : String;
-  P : PAccount;
-  i,iAcc : Integer;
+var
+  LErrors : String;
+  LAccountPtr : PAccount;
+  i,LAccountNo : Integer;
   LKey : TWalletKey;
-  ops : TOperationsHashTree;
-  op : TPCOperation;
-  account,signerAccount,destAccount,accountToBuy : TAccount;
-  operation_to_string, operationstxt, auxs : String;
-  _amount,_fee, _totalamount, _totalfee, _totalSignerFee, _salePrice : Int64;
-  _lockedUntil, _signer_n_ops : Cardinal;
-  dooperation : Boolean;
-  _newOwnerPublicKey : TECDSA_Public;
+  LOperations : TOperationsHashTree;
+  LOperation : TPCOperation;
+  LAccount,LSignerAccount,LTargetAccount,LPurchaseAccount : TAccount;
+  LTargetEPASA : TEPasa;
+  LOperationString, LOperationsTxt, LAuxStr : String;
+  LAmount, LFee, LTotalamount, LTotalFee, LTotalSignerFee, LSalePrice : Int64;
+  LLockedUntil, LSignerNOps : Cardinal;
+  LDoOperation : Boolean;
+  LNewOwnerPublicKey : TECDSA_Public;
   LHashLock : T32Bytes;
-  _newName, LNewAccountData : TRawBytes;
-  _newType : Word;
-  _changeName, _changeType, LChangeAccountData, _V2, _executeSigner, LRecipientSigned : Boolean;
-  _senderAccounts : TCardinalsArray;
+  LNewName, LNewAccountData : TRawBytes;
+  LNewType : Word;
+  LChangeName, LChangeType, LChangeAccountData, LIsV2, LExecuteSigner, LRecipientSigned, LTargetRequiresPurchase : Boolean;
+  LSenderAccounts : TCardinalsArray;
 label loop_start;
 begin
   if Not Assigned(WalletKeys) then raise Exception.Create('No wallet keys');
-  If Not UpdateOperationOptions(errors) then raise Exception.Create(errors);
-  ops := TOperationsHashTree.Create;
+  If Not UpdateOperationOptions(LErrors) then raise Exception.Create(LErrors);
+  LOperations := TOperationsHashTree.Create;
   Try
-    _V2 := FNode.Bank.SafeBox.CurrentProtocol >= CT_PROTOCOL_2;
-    _totalamount := 0;
-    _totalfee := 0;
-    _totalSignerFee := 0;
-    _signer_n_ops := 0;
-    operationstxt := '';
-    operation_to_string := '';
+    LIsV2 := FNode.Bank.SafeBox.CurrentProtocol >= CT_PROTOCOL_2;
+    LTotalamount := 0;
+    LTotalFee := 0;
+    LTotalSignerFee := 0;
+    LSignerNOps := 0;
+    LOperationsTxt := '';
+    LOperationString := '';
 
     // Compile FSenderAccounts into a reorderable array
-    _senderAccounts := FSenderAccounts.ToArray;
+    LSenderAccounts := FSenderAccounts.ToArray;
 
     // Loop through each sender account
-    for iAcc := 0 to Length(_senderAccounts) - 1 do begin
+    for LAccountNo := 0 to Length(LSenderAccounts) - 1 do begin
 loop_start:
-      op := Nil;
-      account := FNode.GetMempoolAccount(_senderAccounts[iAcc]);
-      If Not UpdatePayload(account, errors) then
-        raise Exception.Create('Error encoding payload of sender account '+TAccountComp.AccountNumberToAccountTxtNumber(account.account)+': '+errors);
-      if NOT WalletKeys.TryGetKey(account.accountInfo.accountKey, LKey) then begin
+      LOperation := Nil;
+      LAccount := FNode.GetMempoolAccount(LSenderAccounts[LAccountNo]);
+      If Not UpdatePayload(LAccount, LErrors) then
+        raise Exception.Create('Error encoding payload of sender account '+TAccountComp.AccountNumberToAccountTxtNumber(LAccount.account)+': '+LErrors);
+      if NOT WalletKeys.TryGetKey(LAccount.accountInfo.accountKey, LKey) then begin
 
         if  (
-             (TAccountComp.IsAccountForPrivateSale(account.accountInfo)) or
-             (TAccountComp.IsAccountForAccountSwap(account.accountInfo))
+             (TAccountComp.IsAccountForPrivateSale(LAccount.accountInfo)) or
+             (TAccountComp.IsAccountForAccountSwap(LAccount.accountInfo))
              )
-            and (Not TAccountComp.IsNullAccountKey(account.accountInfo.new_publicKey)) then begin
+            and (Not TAccountComp.IsNullAccountKey(LAccount.accountInfo.new_publicKey)) then begin
 
-          if NOT WalletKeys.TryGetKey(account.accountInfo.new_publicKey, LKey) then
+          if NOT WalletKeys.TryGetKey(LAccount.accountInfo.new_publicKey, LKey) then
             Raise Exception.Create('New sender account private key not found in Wallet');
         end else Raise Exception.Create('Sender account private key not found in Wallet');
       end;
-      dooperation := true;
+      LDoOperation := true;
       // Default fee
-      if account.balance > uint64(DefaultFee) then _fee := DefaultFee else _fee := account.balance;
+      if LAccount.balance > uint64(DefaultFee) then LFee := DefaultFee else LFee := LAccount.balance;
       // Determine which operation type it is
       if PageControlOpType.ActivePage = tsTransaction then begin
         {%region Operation: Transaction}
-        if Not UpdateOpTransaction(account,destAccount,_amount,errors) then raise Exception.Create(errors);
-        if Length(_senderAccounts) > 1 then begin
-          if account.balance>0 then begin
-            if account.balance>DefaultFee then begin
-              _amount := account.balance - DefaultFee;
-              _fee := DefaultFee;
+        if Not UpdateOpTransaction(LAccount, LTargetEPASA, LTargetAccount,LNewOwnerPublicKey, LTargetRequiresPurchase, LAmount, LErrors) then
+          raise Exception.Create(LErrors);
+
+        if Length(LSenderAccounts) > 1 then begin
+          if LAccount.balance>0 then begin
+            if LAccount.balance>DefaultFee then begin
+              LAmount := LAccount.balance - DefaultFee;
+              LFee := DefaultFee;
             end else begin
-              _amount := account.balance;
-              _fee := 0;
+              LAmount := LAccount.balance;
+              LFee := 0;
             end;
-          end else dooperation := false;
-        end else begin
+          end else LDoOperation := false;
         end;
-        if dooperation then begin
-          op := TOpTransaction.CreateTransaction(FNode.Bank.Safebox.CurrentProtocol,account.account,account.n_operation+1,destAccount.account,LKey.PrivateKey,_amount,_fee,FEncodedPayload);
-          inc(_totalamount,_amount);
-          inc(_totalfee,_fee);
+
+        if LDoOperation then begin
+          if NOT LTargetRequiresPurchase then begin
+            LOperation := TOpTransaction.CreateTransaction(FNode.Bank.Safebox.CurrentProtocol,LAccount.account,LAccount.n_operation+1,LTargetAccount.account,LKey.PrivateKey,LAmount,LFee,FEncodedPayload);
+            LOperationsTxt := 'Transaction to '+TAccountComp.AccountNumberToAccountTxtNumber(LTargetAccount.account);
+          end else begin
+            // Pay-to-Key
+            LOperation := TOpBuyAccount.CreateBuy(FNode.Bank.SafeBox.CurrentProtocol, LAccount.account, LAccount.n_operation + 1, LTargetAccount.account, LTargetAccount.accountInfo.account_to_pay, LTargetAccount.accountInfo.price, LAmount, LFee, LNewOwnerPublicKey, LKey.PrivateKey, FEncodedPayload);
+          end;
+         inc(LTotalamount,LAmount);
+         inc(LTotalFee,LFee);
         end;
-        operationstxt := 'Transaction to '+TAccountComp.AccountNumberToAccountTxtNumber(destAccount.account);
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsChangePrivateKey) then begin
         {%region Operation: Change Private Key}
-        if Not UpdateOpChangeKey(account,signerAccount,_newOwnerPublicKey,errors) then raise Exception.Create(errors);
-        if _V2 then begin
+        if Not UpdateOpChangeKey(LAccount,LSignerAccount,LNewOwnerPublicKey,LErrors) then raise Exception.Create(LErrors);
+        if LIsV2 then begin
           // must ensure is Signer account last if included in sender accounts (not necessarily ordered enumeration)
-          if (iAcc < Length(_senderAccounts) - 1) AND (account.account = signerAccount.account) then begin
-            TArrayTool<Cardinal>.Swap(_senderAccounts, iAcc, Length(_senderAccounts) - 1); // ensure signer account processed last
+          if (LAccountNo < Length(LSenderAccounts) - 1) AND (LAccount.account = LSignerAccount.account) then begin
+            TArrayTool<Cardinal>.Swap(LSenderAccounts, LAccountNo, Length(LSenderAccounts) - 1); // ensure signer account processed last
             goto loop_start; // TODO: remove ugly hack with refactoring!
           end;
 
           // Maintain correct signer fee distribution
-          if uint64(_totalSignerFee) >= signerAccount.balance then _fee := 0
-          else if signerAccount.balance - uint64(_totalSignerFee) > uint64(DefaultFee) then _fee := DefaultFee
-          else _fee := signerAccount.balance - uint64(_totalSignerFee);
-          op := TOpChangeKeySigned.Create(FNode.Bank.SafeBox.CurrentProtocol,signerAccount.account,signerAccount.n_operation+_signer_n_ops+1,account.account,LKey.PrivateKey,_newOwnerPublicKey,_fee,FEncodedPayload);
-          inc(_signer_n_ops);
-          inc(_totalSignerFee, _fee);
+          if uint64(LTotalSignerFee) >= LSignerAccount.balance then LFee := 0
+          else if LSignerAccount.balance - uint64(LTotalSignerFee) > uint64(DefaultFee) then LFee := DefaultFee
+          else LFee := LSignerAccount.balance - uint64(LTotalSignerFee);
+          LOperation := TOpChangeKeySigned.Create(FNode.Bank.SafeBox.CurrentProtocol,LSignerAccount.account,LSignerAccount.n_operation+LSignerNOps+1,LAccount.account,LKey.PrivateKey,LNewOwnerPublicKey,LFee,FEncodedPayload);
+          inc(LSignerNOps);
+          inc(LTotalSignerFee, LFee);
         end else begin
-          op := TOpChangeKey.Create(FNode.Bank.SafeBox.CurrentProtocol,account.account,account.n_operation+1,account.account,LKey.PrivateKey,_newOwnerPublicKey,_fee,FEncodedPayload);
+          LOperation := TOpChangeKey.Create(FNode.Bank.SafeBox.CurrentProtocol,LAccount.account,LAccount.n_operation+1,LAccount.account,LKey.PrivateKey,LNewOwnerPublicKey,LFee,FEncodedPayload);
         end;
-        inc(_totalfee,_fee);
-        operationstxt := 'Change private key to '+TAccountComp.GetECInfoTxt(_newOwnerPublicKey.EC_OpenSSL_NID);
+        inc(LTotalFee,LFee);
+        LOperationsTxt := 'Change private key to '+TAccountComp.GetECInfoTxt(LNewOwnerPublicKey.EC_OpenSSL_NID);
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsListAccount) then begin
         {%region Operation: List For Sale}
-        If Not UpdateOpListAccount(account,_salePrice,destAccount,signerAccount,_newOwnerPublicKey, _lockedUntil, LHashLock, errors) then raise Exception.Create(errors);
+        If Not UpdateOpListAccount(LAccount,LSalePrice,LTargetAccount,LSignerAccount,LNewOwnerPublicKey, LLockedUntil, LHashLock, LErrors) then raise Exception.Create(LErrors);
         // Special fee account:
-        if signerAccount.balance>DefaultFee then _fee := DefaultFee
-        else _fee := signerAccount.balance;
+        if LSignerAccount.balance>DefaultFee then LFee := DefaultFee
+        else LFee := LSignerAccount.balance;
         if (rbListAccountForPublicSale.Checked) then begin
-          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForSale, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,
-            destAccount.account,CT_TECDSA_Public_Nul,0,LKey.PrivateKey, CT_HashLock_NUL, FEncodedPayload);
+          LOperation := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForSale, LSignerAccount.account,LSignerAccount.n_operation+1+LAccountNo, LAccount.account,LSalePrice,LFee,
+            LTargetAccount.account,CT_TECDSA_Public_Nul,0,LKey.PrivateKey, CT_HashLock_NUL, FEncodedPayload);
         end else if (rbListAccountForPrivateSale.Checked) then begin
-          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForSale, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,
-            destAccount.account,_newOwnerPublicKey,_lockedUntil,LKey.PrivateKey, CT_HashLock_NUL, FEncodedPayload);
+          LOperation := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForSale, LSignerAccount.account,LSignerAccount.n_operation+1+LAccountNo, LAccount.account,LSalePrice,LFee,
+            LTargetAccount.account,LNewOwnerPublicKey,LLockedUntil,LKey.PrivateKey, CT_HashLock_NUL, FEncodedPayload);
         end  else if (rbListAccountForAccountSwap.Checked) then begin
-          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForAtomicAccountSwap, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,
-            destAccount.account,_newOwnerPublicKey,_lockedUntil,LKey.PrivateKey, LHashLock, FEncodedPayload);
+          LOperation := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForAtomicAccountSwap, LSignerAccount.account,LSignerAccount.n_operation+1+LAccountNo, LAccount.account,LSalePrice,LFee,
+            LTargetAccount.account,LNewOwnerPublicKey,LLockedUntil,LKey.PrivateKey, LHashLock, FEncodedPayload);
         end  else if (rbListAccountForCoinSwap.Checked) then begin
-          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForAtomicCoinSwap, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,
-            destAccount.account,CT_TECDSA_Public_Nul,_lockedUntil,LKey.PrivateKey, LHashLock, FEncodedPayload);
+          LOperation := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForAtomicCoinSwap, LSignerAccount.account,LSignerAccount.n_operation+1+LAccountNo, LAccount.account,LSalePrice,LFee,
+            LTargetAccount.account,CT_TECDSA_Public_Nul,LLockedUntil,LKey.PrivateKey, LHashLock, FEncodedPayload);
         end else raise Exception.Create('Select Sale type');
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsDelistAccount) then begin
         {%region Operation: Delist For Sale}
-        if Not UpdateOpDelist(account,signerAccount,errors) then raise Exception.Create(errors);
+        if Not UpdateOpDelist(LAccount,LSignerAccount,LErrors) then raise Exception.Create(LErrors);
         // Special fee account:
-        if signerAccount.balance>DefaultFee then _fee := DefaultFee
-        else _fee := signerAccount.balance;
-        op := TOpDelistAccountForSale.CreateDelistAccountForSale(FNode.Bank.SafeBox.CurrentProtocol,signerAccount.account,signerAccount.n_operation+1+iAcc,account.account,_fee,LKey.PrivateKey,FEncodedPayload);
+        if LSignerAccount.balance>DefaultFee then LFee := DefaultFee
+        else LFee := LSignerAccount.balance;
+        LOperation := TOpDelistAccountForSale.CreateDelistAccountForSale(FNode.Bank.SafeBox.CurrentProtocol,LSignerAccount.account,LSignerAccount.n_operation+1+LAccountNo,LAccount.account,LFee,LKey.PrivateKey,FEncodedPayload);
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsBuyAccount) then begin
         {%region Operation: Buy Account}
-        if Not UpdateOpBuyAccount(account,accountToBuy,_amount,_newOwnerPublicKey, LRecipientSigned, errors) then raise Exception.Create(errors);
-        if LRecipientSigned AND (NOT WalletKeys.TryGetKey(account.accountInfo.new_publicKey, LKey)) then
+        if Not UpdateOpBuyAccount(LAccount,LPurchaseAccount,LAmount,LNewOwnerPublicKey, LRecipientSigned, LErrors) then raise Exception.Create(LErrors);
+        if LRecipientSigned AND (NOT WalletKeys.TryGetKey(LAccount.accountInfo.new_publicKey, LKey)) then
           raise Exception.Create('Recipient-signed key not found in Wallet');
-        op := TOpBuyAccount.CreateBuy(FNode.Bank.Safebox.CurrentProtocol,account.account,account.n_operation+1,accountToBuy.account,accountToBuy.accountInfo.account_to_pay,
-          accountToBuy.accountInfo.price,_amount,_fee,_newOwnerPublicKey,LKey.PrivateKey,FEncodedPayload);
+        LOperation := TOpBuyAccount.CreateBuy(FNode.Bank.Safebox.CurrentProtocol,LAccount.account,LAccount.n_operation+1,LPurchaseAccount.account,LPurchaseAccount.accountInfo.account_to_pay,
+          LPurchaseAccount.accountInfo.price,LAmount,LFee,LNewOwnerPublicKey,LKey.PrivateKey,FEncodedPayload);
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsChangeInfo) then begin
         {%region Operation: Change Info}
-        if not UpdateOpChangeInfo(account,signerAccount,_changeName,_newName,_changeType,_newType,LChangeAccountData,LNewAccountData,errors) then begin
-          If Length(_senderAccounts)=1 then raise Exception.Create(errors);
+        if not UpdateOpChangeInfo(LAccount,LSignerAccount,LChangeName,LNewName,LChangeType,LNewType,LChangeAccountData,LNewAccountData,LErrors) then begin
+          If Length(LSenderAccounts)=1 then raise Exception.Create(LErrors);
         end else begin
-          if signerAccount.balance>DefaultFee then _fee := DefaultFee
-          else _fee := signerAccount.balance;
-          op := TOpChangeAccountInfo.CreateChangeAccountInfo(FNode.Bank.SafeBox.CurrentProtocol,signerAccount.account,signerAccount.n_operation+1,account.account,LKey.PrivateKey,false,CT_TECDSA_Public_Nul,
-             _changeName,_newName,_changeType,_newType,
+          if LSignerAccount.balance>DefaultFee then LFee := DefaultFee
+          else LFee := LSignerAccount.balance;
+          LOperation := TOpChangeAccountInfo.CreateChangeAccountInfo(FNode.Bank.SafeBox.CurrentProtocol,LSignerAccount.account,LSignerAccount.n_operation+1,LAccount.account,LKey.PrivateKey,false,CT_TECDSA_Public_Nul,
+             LChangeName,LNewName,LChangeType,LNewType,
              LChangeAccountData,LNewAccountData,
-             _fee,FEncodedPayload);
+             LFee,FEncodedPayload);
         end;
         {%endregion}
       end else begin
         raise Exception.Create('No operation selected');
       end;
-      if Assigned(op) And (dooperation) then begin
-        ops.AddOperationToHashTree(op);
-        if operation_to_string<>'' then operation_to_string := operation_to_string + #10;
-        operation_to_string := operation_to_string + op.ToString;
+      if Assigned(LOperation) And (LDoOperation) then begin
+        LOperations.AddOperationToHashTree(LOperation);
+        if LOperationString<>'' then LOperationString := LOperationString + #10;
+        LOperationString := LOperationString + LOperation.ToString;
       end;
-      FreeAndNil(op);
+      FreeAndNil(LOperation);
     end;
 
-    if (ops.OperationsCount=0) then raise Exception.Create('No valid operation to execute');
+    if (LOperations.OperationsCount=0) then raise Exception.Create('No valid operation to execute');
 
-    if (Length(_senderAccounts)>1) then begin
-      if PageControlOpType.ActivePage = tsTransaction then auxs := 'Total amount that dest will receive: '+TAccountComp.FormatMoney(_totalamount)+#10
-      else auxs:='';
-      if Application.MessageBox(PChar('Execute '+Inttostr(Length(_senderAccounts))+' operations?'+#10+
-        'Operation: '+operationstxt+#10+
-        auxs+
-        'Total fee: '+TAccountComp.FormatMoney(_totalfee)+#10+#10+'Note: This operation will be transmitted to the network!'),
+    if (Length(LSenderAccounts)>1) then begin
+      if PageControlOpType.ActivePage = tsTransaction then LAuxStr := 'Total amount that dest will receive: '+TAccountComp.FormatMoney(LTotalamount)+#10
+      else LAuxStr:='';
+      if Application.MessageBox(PChar('Execute '+Inttostr(Length(LSenderAccounts))+' operations?'+#10+
+        'Operation: '+LOperationsTxt+#10+
+        LAuxStr+
+        'Total fee: '+TAccountComp.FormatMoney(LTotalFee)+#10+#10+'Note: This operation will be transmitted to the network!'),
         PChar(Application.Title),MB_YESNO+MB_ICONINFORMATION+MB_DEFBUTTON2)<>IdYes then exit;
     end else begin
-      if Application.MessageBox(PChar('Execute this operation:'+#10+#10+operation_to_string+#10+#10+'Note: This operation will be transmitted to the network!'),
+      if Application.MessageBox(PChar('Execute this operation:'+#10+#10+LOperationString+#10+#10+'Note: This operation will be transmitted to the network!'),
         PChar(Application.Title),MB_YESNO+MB_ICONINFORMATION+MB_DEFBUTTON2)<>IdYes then exit;
     end;
-    i := FNode.AddOperations(nil,ops,Nil,errors);
-    if (i=ops.OperationsCount) then begin
-      operationstxt := 'Successfully executed '+inttostr(i)+' operations!'+#10+#10+operation_to_string;
+    i := FNode.AddOperations(nil,LOperations,Nil,LErrors);
+    if (i=LOperations.OperationsCount) then begin
+      LOperationsTxt := 'Successfully executed '+inttostr(i)+' operations!'+#10+#10+LOperationString;
       If i>1 then begin
         With TFRMMemoText.Create(Self) do
         Try
-          InitData(Application.Title,operationstxt);
+          InitData(Application.Title,LOperationsTxt);
           ShowModal;
         finally
           Free;
         end;
       end else begin
-        Application.MessageBox(PChar('Successfully executed '+inttostr(i)+' operations!'+#10+#10+operation_to_string),PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
+        Application.MessageBox(PChar('Successfully executed '+inttostr(i)+' operations!'+#10+#10+LOperationString),PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
       end;
       ModalResult := MrOk;
     end else if (i>0) then begin
-      operationstxt := 'One or more of your operations has not been executed:'+#10+
+      LOperationsTxt := 'One or more of your operations has not been executed:'+#10+
         'Errors:'+#10+
-        errors+#10+#10+
+        LErrors+#10+#10+
         'Total successfully executed operations: '+inttostr(i);
       With TFRMMemoText.Create(Self) do
       Try
-        InitData(Application.Title,operationstxt);
+        InitData(Application.Title,LOperationsTxt);
         ShowModal;
       finally
         Free;
       end;
       ModalResult := MrOk;
     end else begin
-      raise Exception.Create(errors);
+      raise Exception.Create(LErrors);
     end;
   Finally
-    ops.Free;
+    LOperations.Free;
   End;
 end;
 
@@ -461,20 +473,60 @@ begin
 end;
 
 
+function TFRMOperation.CaptureEPasa(const AEPasaTxt: String; out AEPasa: TEPasa): Boolean;
+var LEPasaErrorCode : EPasaErrorCode;
+begin
+  Result := TEPasa.TryParse(AEPasaTxt,True,AEPasa);
+  //
+  if ((FUpdating) or (Not Result)) then Exit;
+  FUpdating := True;
+  try
+    if AEPasa.PayloadType.HasTrait(ptPublic) then rbNotEncrypted.Checked := True
+    else if AEPasa.PayloadType.HasTrait(ptSenderKeyEncrypted) then rbEncryptedWithOldEC.Checked := True
+    else if AEPasa.PayloadType.HasTrait(ptRecipientKeyEncrypted) then rbEncryptedWithEC.Checked := True
+    else if AEPasa.PayloadType.HasTrait(ptPasswordEncrypted) then begin
+      rbEncrptedWithPassword.Checked := True;
+      ebEncryptPassword.Text := AEPasa.Password;
+    end;
+    if AEPasa.PayloadType.HasTrait(ptAsciiFormatted) then cbPayloadDataInputType.ItemIndex := 0
+    else if AEPasa.PayloadType.HasTrait(ptHexFormatted) then cbPayloadDataInputType.ItemIndex := 1
+    else if AEPasa.PayloadType.HasTrait(ptBase58Formatted) then cbPayloadDataInputType.ItemIndex := 2;
+    if (AEPasa.PayloadType.HasTrait(ptAsciiFormatted) or AEPasa.PayloadType.HasTrait(ptHexFormatted) or AEPasa.PayloadType.HasTrait(ptBase58Formatted)) then begin
+      memoPayload.Lines.Text := AEPasa.Payload;
+    end;
+
+    memoEPASA.Lines.Clear;
+    if AEPasa.IsPayToKey then memoEPASA.Lines.Add('PayToKey EPASA');
+    if AEPasa.IsAddressedByName then memoEPASA.Lines.Add('Addressed by name: '+AEPasa.AccountName);
+    memoEPASA.Lines.Add( AEPasa.ToString );
+    if AEPasa.PayloadType.HasTrait(ptAsciiFormatted) or AEPasa.PayloadType.HasTrait(ptBase58Formatted) or AEPasa.PayloadType.HasTrait(ptHexFormatted) then begin
+      memoEPasa.Lines.Add( 'AS BYTES: 0x'+AEPasa.GetRawPayloadBytes.ToHexaString);
+      memoEPasa.Lines.Add( 'AS PRINTABLE: '+AEPasa.GetRawPayloadBytes.ToPrintable);
+    end;
+
+  finally
+    FUpdating := False;
+  end;
+end;
+
 procedure TFRMOperation.CM_WalletChanged(var Msg: TMessage);
 begin
    UpdateWalletKeys;
 end;
 
 procedure TFRMOperation.ebAccountNumberExit(Sender: TObject);
-Var an : Cardinal;
+Var LEPasa : TEPASA;
   eb : TEdit;
 begin
-  if (Not assigned(Sender)) then exit;
+  if (Not assigned(Sender)) or (FUpdating) then exit;
   if (Not (Sender is TEdit)) then exit;
   eb := TEdit(Sender);
-  If TAccountComp.AccountTxtNumberToAccountNumber(eb.Text,an) then begin
-    eb.Text := TAccountComp.AccountNumberToAccountTxtNumber(an);
+  if CaptureEPasa(eb.Text,LEPasa) then begin
+    if LEPasa.IsClassicPASA then
+      eb.Text := LEPasa.ToClassicPASAString()
+    else if eb=ebDestAccount then
+      eb.Text := LEPasa.ToString(False)
+    else eb.Text := LEPasa.ToString(True);
   end else begin
     eb.Text := '';
   end;
@@ -485,7 +537,7 @@ procedure TFRMOperation.ebCurrencyExit(Sender: TObject);
 Var m : Int64;
   eb : TEdit;
 begin
-  if (Not assigned(Sender)) then exit;
+  if (Not assigned(Sender)) or (FUpdating) then exit;
   if (Not (Sender is TEdit)) then exit;
   eb := TEdit(Sender);
   If Not (eb.ReadOnly) then begin
@@ -532,10 +584,10 @@ procedure TFRMOperation.FormCreate(Sender: TObject);
 begin
   {$IFDEF USE_GNUGETTEXT}TranslateComponent(self);{$ENDIF}
   FDisabled := false;
+  FUpdating := False;
   FWalletKeys := Nil;
   FSenderAccounts := TOrderedCardinalList.Create;
   FSenderAccounts.OnListChanged := OnSenderAccountsChanged;
-  FDisabled := true;
   FNode := TNode.Node;
   ebSenderAccount.OnKeyDown:=ebAccountKeyDown;
   ebSenderAccount.Tag:=CT_AS_MyAccounts;
@@ -608,6 +660,24 @@ begin
   ebFee.OnExit:= ebCurrencyExit;
   memoAccounts.Lines.Clear;
   PageControlOpType.ActivePage := tsTransaction;
+  cbPayloadDataInputType.Items.Clear;
+  cbPayloadDataInputType.Items.Add('As String');
+  cbPayloadDataInputType.Items.Add('As Hexadecimal');
+  cbPayloadDataInputType.Items.Add('As Base58');
+  cbPayloadDataInputType.ItemIndex := 0;
+  cbPayloadDataInputType.OnChange := memoPayloadClick;
+  memoPayload.OnChange := memoPayloadClick;
+  memoPayload.OnClick := memoPayloadClick;
+  memoEPASA.ScrollBars := ssBoth;
+  memoEPASA.ReadOnly := False;
+  memoEPASA.TabStop := False;
+  memoEPASA.ParentFont := True;
+  {$IFDEF TESTNET}
+  memoEPASA.Visible := True;
+  {$ELSE}
+  memoEPASA.Visible := False;
+  {$ENDIF}
+  memoEPASA.Enabled := memoEPASA.Visible;
 end;
 
 procedure TFRMOperation.ebNewPublicKeyExit(Sender: TObject);
@@ -640,14 +710,16 @@ end;
 
 procedure TFRMOperation.searchAccount(editBox: TCustomEdit);
 Var F : TFRMAccountSelect;
-  c : Cardinal;
+  LEPasa : TEPasa;
 begin
   F := TFRMAccountSelect.Create(Self);
   try
     F.Node := FNode;
     F.WalletKeys := FWalletKeys;
     F.Filters:=editBox.Tag;
-    If TAccountComp.AccountTxtNumberToAccountNumber(editBox.Text,c) then F.DefaultAccount := c;
+    if TEPasa.TryParse(editBox.Text,LEPasa) then begin
+      if LEPasa.Account.HasValue then F.DefaultAccount := LEPasa.Account.Value
+    end;
     F.AllowSelect:=True;
     If F.ShowModal=MrOk then begin
       editBox.Text := TAccountComp.AccountNumberToAccountTxtNumber(F.GetSelected);
@@ -840,17 +912,15 @@ begin
   end;
 end;
 
-var GInUpdateInfoClick : boolean;
 procedure TFRMOperation.updateInfoClick(Sender: TObject);
 Var errors : String;
 begin
-  if NOT GInUpdateInfoClick then begin
-    GInUpdateInfoClick := true;
-    try
-      UpdateOperationOptions(errors);
-    finally
-    GInUpdateInfoClick := false;
-    end;
+  if FDisabled or FUpdating then Exit;
+  FDisabled := True;
+  try
+    UpdateOperationOptions(errors);
+  finally
+    FDisabled := False;
   end;
 end;
 
@@ -1147,6 +1217,8 @@ Var
   changeName,changeType, LRecipientSigned, LChangeAccountData : Boolean;
   newName, LNewAccountData : TRawBytes;
   newType : Word;
+  LTargetEPASA : TEPasa;
+  LRequiresPurchase : Boolean;
 begin
   Result := false;
   sender_account := CT_Account_NUL;
@@ -1212,7 +1284,7 @@ begin
     end;
   End;
   if (PageControlOpType.ActivePage = tsTransaction) then begin
-    Result := UpdateOpTransaction(GetDefaultSenderAccount,dest_account,amount,errors);
+    Result := UpdateOpTransaction(GetDefaultSenderAccount,LTargetEPASA, dest_account, publicKey, LRequiresPurchase, amount,errors);
   end else if (PageControlOpType.ActivePage = tsChangePrivateKey) then begin
     Result := UpdateOpChangeKey(GetDefaultSenderAccount,signer_account,publicKey,errors);
   end else if (PageControlOpType.ActivePage = tsListAccount) then begin
@@ -1247,8 +1319,6 @@ begin
   lblSignerAccount.Enabled := ebSignerAccount.Enabled;
   lblChangeName.Enabled:= (PageControlOpType.ActivePage=tsChangeInfo) And (SenderAccounts.Count=1);
   ebChangeName.Enabled:= lblChangeName.Enabled;
-  //
-  UpdatePayload(sender_account, e);
 end;
 
 function TFRMOperation.UpdateOpListAccount(const TargetAccount: TAccount;
@@ -1508,177 +1578,249 @@ begin
   End;
 end;
 
-function TFRMOperation.UpdateOpTransaction(const SenderAccount: TAccount;  var DestAccount: TAccount; var amount: Int64;  var errors: String): Boolean;
-Var c : Cardinal;
+function TFRMOperation.UpdateOpTransaction(const ASenderAccount: TAccount; out ATargetEPASA : TEPasa; out ATargetAccount : TAccount; out AResolvedTargetKey : TECDSA_Public; out ATargetRequiresPurchase : Boolean; out AAmount: Int64; out AErrors: String): Boolean;
+Var
+  LResolvedAccountNo : Cardinal;
+  LPublicKey : TAccountKey;
 begin
-  Result := False;
-  errors := '';
+  AErrors := '';
   lblTransactionErrors.Caption := '';
   if PageControlOpType.ActivePage<>tsTransaction then exit;
-  if not (TAccountComp.AccountTxtNumberToAccountNumber(ebDestAccount.Text,c)) then begin
-    errors := 'Invalid dest. account ('+ebDestAccount.Text+')';
-    lblTransactionErrors.Caption := errors;
-    exit;
+
+  if (Trim(ebDestAccount.Text)='') then begin
+    AErrors := 'Need a destintation or EPASA';
+    lblTransactionErrors.Caption := AErrors;
+    Exit(False);
+  end;
+
+
+  if Not CaptureEPasa(ebDestAccount.Text,ATargetEPasa) then begin
+    AErrors := 'Invalid EPASA value: '+ebDestAccount.Text;
+    lblTransactionErrors.Caption := AErrors;
+    Exit(False);
   end;
-  if (c<0) Or (c>=TNode.Node.Bank.AccountsCount) then begin
-    errors := 'Invalid dest. account ('+TAccountComp.AccountNumberToAccountTxtNumber(c)+')';
-    lblTransactionErrors.Caption := errors;
-    exit;
+
+  Result := TNode.Node.TryResolveEPASA(ATargetEPASA, LResolvedAccountNo, AResolvedTargetKey, ATargetRequiresPurchase, AErrors);
+  if NOT Result then begin
+    lblTransactionErrors.Caption := AErrors;
+    Exit(False);
   end;
-  DestAccount := TNode.Node.GetMempoolAccount(c);
-  if SenderAccounts.Count=1 then begin
-    if not TAccountComp.TxtToMoney(ebAmount.Text,amount) then begin
-      errors := 'Invalid amount ('+ebAmount.Text+')';
-      lblTransactionErrors.Caption := errors;
-      exit;
+
+  // GUI Base58 protection: In order to prevent manual mistake, Base58 is only allowed when introducing
+  // a Public key, otherwise will need to use String ("") or Hexadecimal (0x..) input
+  if (ATargetEPASA.PayloadType.HasTrait(ptBase58Formatted)) then begin
+     if Not TAccountComp.AccountPublicKeyImport(ATargetEPASA.Payload,LPublicKey,AErrors) then begin
+       AErrors := 'Not a Base58 Public key: '+AErrors;
+       lblTransactionErrors.Caption := AErrors;
+       Exit(False);
+     end;
+  end;
+
+  if LResolvedAccountNo <> CT_AccountNo_NUL then begin
+    ATargetAccount := TNode.Node.GetMempoolAccount(LResolvedAccountNo);
+    if ATargetAccount.account=ASenderAccount.account then begin
+      AErrors := 'Sender and dest account are the same';
+      lblTransactionErrors.Caption := AErrors;
+      Exit(False);
     end;
-  end else amount := 0; // ALL BALANCE
-  if DestAccount.account=SenderAccount.account then begin
-    errors := 'Sender and dest account are the same';
-    lblTransactionErrors.Caption := errors;
-    exit;
   end;
+
+  if SenderAccounts.Count=1 then begin
+    if not TAccountComp.TxtToMoney(ebAmount.Text,AAmount) then begin
+      AErrors := 'Invalid amount ('+ebAmount.Text+')';
+      lblTransactionErrors.Caption := AErrors;
+      Exit(False);
+    end;
+  end else AAmount := 0; // ALL BALANCE
+
+
   if (SenderAccounts.Count=1) then begin
-    if (SenderAccount.balance<(amount+FDefaultFee)) then begin
-       errors := 'Insufficient funds';
-       lblTransactionErrors.Caption := errors;
-       exit;
+    if (ASenderAccount.balance<(AAmount+FDefaultFee)) then begin
+       AErrors := 'Insufficient funds';
+       lblTransactionErrors.Caption := AErrors;
+       Exit(False);
     end;
   end;
-  Result := True;
 end;
 
-function TFRMOperation.UpdatePayload(const SenderAccount: TAccount;
-  var errors: String): Boolean;
-Var payload_u : AnsiString;
-  payload_encrypted : TRawBytes;
-  account : TAccount;
-  public_key : TAccountKey;
-  dest_account_number : Cardinal;
+function TFRMOperation.UpdatePayload(const ASenderAccount: TAccount; var AErrors: String): Boolean;
+Var
+  LUserPayloadString : AnsiString;
+  LEncryptedPayloadBytes : TRawBytes;
+  LAccount : TAccount;
+  LTargetEPASA : TEPasa;
+  LPublicKey : TAccountKey;
+  LAccountNumber : Cardinal;
   i : Integer;
-  valid : Boolean;
-  wk : TWalletKey;
+  LValid : Boolean;
+  LWalletKey : TWalletKey;
+  LPassword : String;
   LPayloadBytes : TRawBytes;
 begin
-  valid := false;
-  payload_encrypted := Nil;
+  LValid := false;
+  LPayloadBytes := Nil;
+  LEncryptedPayloadBytes := Nil;
   FEncodedPayload := CT_TOperationPayload_NUL;
-  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-  // TODO:
-  // Needs to assign FEncodedPayload.payload_type based on PIP-0027
-  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-  errors := 'Unknown error';
-  payload_u := memoPayload.Lines.Text;
+  LUserPayloadString := memoPayload.Lines.Text;
+  FEncodedPayload.payload_type := 0; // [ptNonDeterministic]
+  AErrors := 'Unknown error';
   try
-    if (payload_u='') then begin
-      valid := true;
-      exit;
+    LTargetEPASA := TEPasa.Empty;
+
+    If (PageControlOpType.ActivePage=tsTransaction) then begin
+      if NOT TEPasa.TryParse(ebDestAccount.Text,True, LTargetEPASA) then begin
+        AErrors := 'Indeterminable target';
+        Exit(False);
+      end;
     end;
-    if cbPayloadAsHex.Checked then begin
-      if NOT TCrypto.HexaToRaw(payload_u, LPayloadBytes) then begin
-        valid := false;
-        errors := 'Payload not hex-formatted';
-        exit;
+
+    if (LUserPayloadString='') then begin
+      LValid := True;
+      Exit(True);
+    end;
+
+    LTargetEPASA.PayloadType := LTargetEPASA.PayloadType - [ptPublic] - [ptRecipientKeyEncrypted] - [ptSenderKeyEncrypted]
+      - [ptPasswordEncrypted] - [ptAsciiFormatted] - [ptHexFormatted] - [ptBase58Formatted];
+
+    case cbPayloadDataInputType.ItemIndex of
+      0 : begin
+           LTargetEPASA.PayloadType := LTargetEPASA.PayloadType + [ptAsciiFormatted];
+           LPayloadBytes := TEncoding.ASCII.GetBytes(LUserPayloadString);
+      end;
+      1 : begin
+         LTargetEPASA.PayloadType := LTargetEPASA.PayloadType + [ptHexFormatted];
+         if Not THexEncoding.TryDecode(LUserPayloadString,LPayloadBytes) then begin
+           AErrors := 'Payload is not an Hexadecimal string';
+           Exit(False);
+         end;
       end;
-    end else LPayloadBytes := TEncoding.ANSI.GetBytes(payload_u);
+      2 : begin
+         LTargetEPASA.PayloadType := LTargetEPASA.PayloadType + [ptBase58Formatted];
+         if Not TPascalBase58Encoding.TryDecode(LUserPayloadString,LPayloadBytes) then begin
+           AErrors := 'Payload is not a Base 58 string';
+           Exit(False);
+         end;
+
+         // GUI Base58 protection: In order to prevent manual mistake, Base58 is only allowed when introducing
+         // a Public key, otherwise will need to use String ("") or Hexadecimal (0x..) input
+         if Not TAccountComp.AccountPublicKeyImport(LUserPayloadString,LPublicKey,AErrors) then begin
+           AErrors := 'Not a Public key: '+AErrors;
+           Exit(False);
+         end;
+      end
+    else
+    end;
+    LTargetEPASA.Payload := LUserPayloadString;
 
-    if (rbEncryptedWithOldEC.Checked) then begin
+    if (Length(LPayloadBytes)>0) and (rbEncryptedWithOldEC.Checked) then begin
       // Use sender
-      errors := 'Error encrypting';
-      account := FNode.GetMempoolAccount(SenderAccount.account);
-      TPCEncryption.DoPascalCoinECIESEncrypt(account.accountInfo.accountKey,LPayloadBytes,payload_encrypted);
-      valid := Length(payload_encrypted)>0;
-    end else if (rbEncryptedWithEC.Checked) then begin
-      errors := 'Error encrypting';
+      LTargetEPASA.PayloadType := LTargetEPASA.PayloadType + [ptSenderKeyEncrypted];
+      AErrors := 'Error encrypting by sender';
+      LAccount := FNode.GetMempoolAccount(ASenderAccount.account);
+      TPCEncryption.DoPascalCoinECIESEncrypt(LAccount.accountInfo.accountKey,LPayloadBytes,LEncryptedPayloadBytes);
+      LValid := Length(LEncryptedPayloadBytes)>0;
+    end else if (Length(LPayloadBytes)>0) and (rbEncryptedWithEC.Checked) then begin
+      AErrors := 'Error encrypting by recipient';
+      LTargetEPASA.PayloadType := LTargetEPASA.PayloadType + [ptRecipientKeyEncrypted];
       if (PageControlOpType.ActivePage=tsTransaction) or (PageControlOpType.ActivePage=tsListAccount) or (PageControlOpType.ActivePage=tsDelistAccount)
         or (PageControlOpType.ActivePage=tsBuyAccount) then begin
+
         // With dest public key
         If (PageControlOpType.ActivePage=tsTransaction) then begin
-          If Not TAccountComp.AccountTxtNumberToAccountNumber(ebDestAccount.Text,dest_account_number) then begin
-            errors := 'Invalid dest account number';
+          If Not Self.FNode.TryResolveEPASA(LTargetEPASA, LAccountNumber) then begin
+            AErrors := 'Invalid dest account EPASA';
             exit;
           end;
         end else if (PageControlOpType.ActivePage=tsListAccount) then begin
-          If Not TAccountComp.AccountTxtNumberToAccountNumber(ebSignerAccount.Text,dest_account_number) then begin
-            errors := 'Invalid signer account number';
+          If Not TAccountComp.AccountTxtNumberToAccountNumber(ebSignerAccount.Text,LAccountNumber) then begin
+            AErrors := 'Invalid signer account number';
             exit;
           end;
         end else if (PageControlOpType.ActivePage=tsDelistAccount) then begin
-          If Not TAccountComp.AccountTxtNumberToAccountNumber(ebSignerAccount.Text,dest_account_number) then begin
-            errors := 'Invalid signer account number';
+          If Not TAccountComp.AccountTxtNumberToAccountNumber(ebSignerAccount.Text,LAccountNumber) then begin
+            AErrors := 'Invalid signer account number';
             exit;
           end;
         end else if (PageControlOpType.ActivePage=tsBuyAccount) then begin
-          If Not TAccountComp.AccountTxtNumberToAccountNumber(ebAccountToBuy.Text,dest_account_number) then begin
-            errors := 'Invalid account to buy number';
+          If Not TAccountComp.AccountTxtNumberToAccountNumber(ebAccountToBuy.Text,LAccountNumber) then begin
+            AErrors := 'Invalid account to buy number';
             exit;
           end;
         end else begin
-          errors := 'ERROR DEV 20170512-1';
+          AErrors := 'ERROR DEV 20170512-1';
           exit;
         end;
-        if (dest_account_number<0) or (dest_account_number>=FNode.Bank.AccountsCount) then begin
-          errors := 'Invalid payload encrypted account number: '+TAccountComp.AccountNumberToAccountTxtNumber(dest_account_number);
+        if (LAccountNumber<0) or (LAccountNumber>=FNode.Bank.AccountsCount) then begin
+          AErrors := 'Invalid payload encrypted account number: '+TAccountComp.AccountNumberToAccountTxtNumber(LAccountNumber);
           exit;
         end;
-        account := FNode.GetMempoolAccount(dest_account_number);
-        TPCEncryption.DoPascalCoinECIESEncrypt(account.accountInfo.accountKey,LPayloadBytes,payload_encrypted);
-        valid := Length(payload_encrypted)>0;
+        LAccount := FNode.GetMempoolAccount(LAccountNumber);
+        TPCEncryption.DoPascalCoinECIESEncrypt(LAccount.accountInfo.accountKey,LPayloadBytes,LEncryptedPayloadBytes);
+        LValid := Length(LEncryptedPayloadBytes)>0;
       end else if (PageControlOpType.ActivePage=tsChangePrivateKey) then begin
         if (rbChangeKeyWithAnother.Checked) then begin
           // With new key generated
           if (cbNewPrivateKey.ItemIndex>=0) then begin
             i := PtrInt(cbNewPrivateKey.Items.Objects[cbNewPrivateKey.ItemIndex]);
-            if (i>=0) then public_key := WalletKeys.Key[i].AccountKey;
+            if (i>=0) then LPublicKey := WalletKeys.Key[i].AccountKey;
           end else begin
-            errors := 'Must select a private key';
+            AErrors := 'Must select a private key';
             exit;
           end;
         end else if (rbChangeKeyTransferAccountToNewOwner.Checked) then begin
-          If Not TAccountComp.AccountKeyFromImport(ebNewPublicKey.Text,public_key,errors) then begin
-            errors := 'Public key: '+errors;
+          If Not TAccountComp.AccountKeyFromImport(ebNewPublicKey.Text,LPublicKey,AErrors) then begin
+            AErrors := 'Public key: '+AErrors;
             exit;
           end;
         end else begin
-          errors := 'Must select change type';
+          AErrors := 'Must select change type';
           exit;
         end;
-        if public_key.EC_OpenSSL_NID<>CT_Account_NUL.accountInfo.accountKey.EC_OpenSSL_NID then begin
-          TPCEncryption.DoPascalCoinECIESEncrypt(public_key,LPayloadBytes,payload_encrypted);
-          valid := Length(payload_encrypted)>0;
+        if LPublicKey.EC_OpenSSL_NID<>CT_Account_NUL.accountInfo.accountKey.EC_OpenSSL_NID then begin
+          TPCEncryption.DoPascalCoinECIESEncrypt(LPublicKey,LPayloadBytes,LEncryptedPayloadBytes);
+          LValid := Length(LEncryptedPayloadBytes)>0;
         end else begin
-          valid := false;
-          errors := 'Selected private key is not valid to encode';
+          LValid := false;
+          AErrors := 'Selected private key is not valid to encode';
           exit;
         end;
       end else begin
-        errors := 'This operation does not allow this kind of payload';
+        AErrors := 'This operation does not allow this kind of payload';
       end;
-    end else if (rbEncrptedWithPassword.Checked) then begin
-      payload_encrypted := TPCEncryption.DoPascalCoinAESEncrypt(LPayloadBytes,TEncoding.ANSI.GetBytes(ebEncryptPassword.Text));
-      valid := Length(payload_encrypted)>0;
-    end else if (rbNotEncrypted.Checked) then begin
-      payload_encrypted := LPayloadBytes;
-      valid := true;
+    end else if (Length(LPayloadBytes)>0) AND (rbEncrptedWithPassword.Checked) then begin
+      LTargetEPASA.PayloadType := LTargetEPASA.PayloadType + [ptPasswordEncrypted];
+      LPassword := ebEncryptPassword.Text;
+      LEncryptedPayloadBytes := TPCEncryption.DoPascalCoinAESEncrypt(LPayloadBytes,TEncoding.ANSI.GetBytes(LPassword));
+      LTargetEPASA.Password := LPassword;
+      LValid := Length(LEncryptedPayloadBytes)>0;
+    end else if (Length(LPayloadBytes)>0) AND (rbNotEncrypted.Checked) then begin
+      LTargetEPASA.PayloadType := LTargetEPASA.PayloadType + [ptPublic];
+      LEncryptedPayloadBytes := LPayloadBytes;
+      LValid := true;
     end else begin
-      errors := 'Must select an encryption option for payload';
+      AErrors := 'Must select an encryption option for payload';
     end;
   finally
-    if valid then begin
-      if length(payload_encrypted)>CT_MaxPayloadSize then begin
-        valid := false;
-        errors := 'Payload size is bigger than '+inttostr(CT_MaxPayloadSize)+' ('+Inttostr(length(payload_encrypted))+')';
+    if LValid then begin
+      if length(LEncryptedPayloadBytes)>CT_MaxPayloadSize then begin
+        LValid := false;
+        AErrors := 'Payload size is bigger than '+inttostr(CT_MaxPayloadSize)+' ('+Inttostr(length(LEncryptedPayloadBytes))+')';
       end;
     end;
-    if valid then begin
+    if LValid then begin
       lblEncryptionErrors.Caption := '';
-      lblPayloadLength.Caption := Format('(%db -> %db)',[length(payload_u),length(payload_encrypted)]);
+      lblPayloadLength.Caption := Format('(%db -> %db)',[length(LTargetEPASA.Payload),length(LEncryptedPayloadBytes)])
     end else begin
-      lblEncryptionErrors.Caption := errors;
-      lblPayloadLength.Caption := Format('(%db -> ?)',[length(payload_u)]);
+      if Trim(AErrors)='' then AErrors := 'Undefined';
+      lblEncryptionErrors.Caption := AErrors;
+      lblPayloadLength.Caption := Format('(%db -> ?)',[length(LTargetEPASA.Payload)])
+    end;
+    FEncodedPayload.payload_type := LTargetEPASA.PayloadType.ToProtocolValue;
+    FEncodedPayload.payload_raw := LEncryptedPayloadBytes;
+    Result := LValid;
+    if (LValid) And (Not FUpdating) then begin
+      ebDestAccount.Text := LTargetEPASA.ToString(False);
     end;
-    FEncodedPayload.payload_raw := payload_encrypted;
-    Result := valid;
   end;
 end;
 

+ 3 - 1
src/gui-classic/UFRMOperationsExplorer.pas

@@ -715,7 +715,9 @@ begin
     If op.OperationToOperationResume(FSourceNode.Bank.BlocksCount,op,True,op.SignerAccount,opr) then begin
       jsonObj := TPCJSONObject.Create;
       Try
-        TPascalCoinJSONComp.FillOperationObject(opr,FSourceNode.Bank.BlocksCount,jsonObj);
+        TPascalCoinJSONComp.FillOperationObject(opr,FSourceNode.Bank.BlocksCount,
+          SourceNode,SourceWalletKeys,Nil,
+          jsonObj);
         mOperationInfo.Lines.Add('JSON:');
         mOperationInfo.Lines.Add(jsonObj.ToJSON(False));
       Finally

+ 4 - 5
src/gui-classic/UFRMPascalCoinWalletConfig.pas

@@ -30,11 +30,10 @@ uses
   LCLIntf, LCLType, LMessages,
 {$ENDIF}
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
-  Dialogs, StdCtrls, Buttons, ComCtrls, UAppParams, UWallet;
+  Dialogs, StdCtrls, Buttons, ComCtrls, UAppParams, USettings, UWallet;
 
 type
 
-  TMinerPrivateKey = (mpk_NewEachTime, mpk_Random, mpk_Selected);
 
   { TFRMPascalCoinWalletConfig }
 
@@ -98,7 +97,7 @@ type
 implementation
 
 uses
-  {$IFDEF USE_GNUGETTEXT}gnugettext, UFRMSelectLanguage, {$ENDIF}UConst, UAccounts, ULog, UCrypto, UNode, USettings, UGUIUtils, UNetProtocol;
+  {$IFDEF USE_GNUGETTEXT}gnugettext, UFRMSelectLanguage, {$ENDIF}UConst, UAccounts, ULog, UCrypto, UNode, UGUIUtils, UNetProtocol;
 
 {$IFnDEF FPC}
   {$R *.dfm}
@@ -108,7 +107,7 @@ uses
 
 procedure TFRMPascalCoinWalletConfig.bbOkClick(Sender: TObject);
 Var df : Int64;
-  mpk : TMinerPrivateKey;
+  mpk : TMinerPrivateKeyType;
   i : Integer;
 begin
   if udInternetServerPort.Position = udJSONRPCMinerServerPort.Position then raise Exception.Create('Server port and JSON-RPC Server miner port are equal!');
@@ -241,7 +240,7 @@ begin
     udInternetServerPort.Position := AppParams.ParamByName[CT_PARAM_InternetServerPort].GetAsInteger(CT_NetServer_Port);
     ebDefaultFee.Text := TAccountComp.FormatMoney(AppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0));
     cbJSONRPCMinerServerActive.Checked := AppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].GetAsBoolean(true);
-    case TMinerPrivateKey(AppParams.ParamByName[CT_PARAM_MinerPrivateKeyType].GetAsInteger(Integer(mpk_Random))) of
+    case TMinerPrivateKeyType(AppParams.ParamByName[CT_PARAM_MinerPrivateKeyType].GetAsInteger(Integer(mpk_Random))) of
       mpk_NewEachTime : rbGenerateANewPrivateKeyEachBlock.Checked := true;
       mpk_Random : rbUseARandomKey.Checked := true;
       mpk_Selected : rbMineAllwaysWithThisKey.Checked := true;

+ 1025 - 0
src/gui-classic/UFRMSplash.dfm

@@ -0,0 +1,1025 @@
+object FRMSplash: TFRMSplash
+  Left = 0
+  Top = 0
+  BorderIcons = []
+  BorderStyle = bsDialog
+  Caption = 'PascalCoin process...'
+  ClientHeight = 188
+  ClientWidth = 542
+  Color = clBtnFace
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
+  Font.Style = []
+  FormStyle = fsStayOnTop
+  OldCreateOrder = True
+  Position = poOwnerFormCenter
+  OnCreate = FormCreate
+  OnDestroy = FormDestroy
+  PixelsPerInch = 96
+  TextHeight = 13
+  object imgSplash: TImage
+    Left = 15
+    Top = 15
+    Width = 100
+    Height = 100
+    Picture.Data = {
+      07544269746D617066750000424D667500000000000036000000280000006400
+      000064000000010018000000000030750000120B0000120B0000000000000000
+      0000FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      F8EAEAD2C8C8F7EAEAFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECF9EBEB959090484848807D7DF5E9E9FBECECFBECECFBEC
+      ECF8EAEADFD4D4FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBEDEDFBECECFBECECFBECECDFD4D44A4A4A4040404141
+      41999494FBECECFBECECFBEDED8985854A4A4AA9A3A3FBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECF4E7E78C88886F6D6DD9CECEFBECECFBED
+      ED8A86864343434444444242424747478D8989B6AEAE837F7F4242423E3E3E50
+      5050E9DDDDFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECB7AFAF4444
+      444040404D4D4D8F8B8B85818146464642424243434343434342424242424244
+      44444343434343434242424C4C4CDCD1D1FBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECB6AEAE45454543434342424242424241414140404042424244
+      4444454545464646454545434343414141434343434343444444BFB7B7FBECEC
+      FBECECF4E6E6FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECE9DDDD55555543434344444441
+      4141434343505050848080AFA8A8B7AFAFB7AFAFB4ADAD9C9696666464474747
+      414141424242605F5FA29C9C948F8F6A6767B9B2B2FBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEDEDBB
+      B4B44C4B4B4242424141415352529F9999E6DADAFBEDEDFBECECFBECECFBECEC
+      FBECECFBECECF2E4E4BDB4B45858584242424242424343434343434040404C4C
+      4CCCC3C3FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECEE
+      E1E1A09A9AA09B9B807C7C4646464242424040406F6D6DE1D6D6FBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECE5D9D97775754141
+      41434343444444434343484848C7BEBEFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECF9EAEA8E8989434343434343424242424242414141525252
+      E4D9D9FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECF5E8E87572724141414545454141417D7979F9EBEBFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECF7E8E87876763F3F3F434343
+      444444424242484848C4BCBCFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECD8CECE4A4A4A42424243
+      4343B4ADADFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECEDE1E1757272414141444444424242848080FBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECEC8A86864242424343439D9898FBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECE6DBDB575656404040484848D0C6
+      C6FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECD5CACA4D4D4D4141414949498E8989
+      C0B7B7F8EAEAFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECEEE2
+      E25857573E3E3E6B6969F6E8E8FBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECE6DADA
+      5554544343434242424141414848489C9797FBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECF9ECEC8884844242424141417F7B7BFBEDEDFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECF3E5E56664644141414444444343433E3E3E706D6DFAEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECE3D7D7716E6E42424243434341414173
+      7070F7E9E9FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC8480804141414343
+      434242425C5B5BC9C0C0FBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECA8A3A33F
+      3F3F434343444444414141787575F8EAEAFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECF4E6E66B68684242424343437D7979EBDFDFFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEDEDD3C8C8
+      C5BCBCC2BABAEEE1E1FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECADA8A8434343414141434343424242525252ECDFDFFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECEADDDD585757414141494949D1C7C7FBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBEDED9C98984A4A4A4848484646465D5C5CBAB3B3FBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECEDE1E1AEA7A77C7979474747
+      414141484848D6CCCCFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECDED3D351515141
+      4141494949D0C6C6FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFAEBEB9F99994444444242424343434343434040
+      404B4B4BD3C9C9FBECECFBECECFBECECFBECECFBECECFBECECFBECECF8EAEAD5
+      CBCBD1C6C6EBDFDFFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBEDED8A86864343434242428C8888FBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECA7A1A1444444434343414141807D7DF5E8E8FBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECEDE0E05352524141
+      414444444444444444444444444141419B9595FBECECFBECECFBECECFBECECFB
+      ECECFBECECF6E9E9827F7F4A4A4A4949495C5B5BAEA7A7FBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECBDB5B54646464141414E4E
+      4EE0D5D5FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECF7EAEA656464414141444444434343414141
+      8A8686FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECE7DBDB4F4F4F424242444444444444444444444444424242575656E2
+      D7D7FBECECFBECECFBECECFBECECFBECECA49E9E424242424242434343414141
+      464646A49E9EFBEDEDFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECA7A0A0434343434343424242686666DCD1D1FBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFAECEC8D8989434343
+      4242424141414242423E3E3E515151D8CECEBEB5B58581818D8888DDD2D2FBEC
+      ECFBECECFBECECFBECECFBECECFBECEC9C969643434344444444444444444444
+      4444444444444444414141575656B8B0B0F9EBEBFBECECFBECECBAB2B24C4C4C
+      424242444444444444444444414141464646BCB4B4FBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECE3D8D85A5959414141444444444444404040565555BE
+      B6B6FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      EFE3E38581814242424141414A4A4A817D7D9590907775754444444949494747
+      474242424141415B5A5AD9CFCFFBECECFBECECFBECECFBECECCDC3C349494942
+      4242444444444444444444444444444444444444444444404040484848787575
+      ADA6A68D8989474747414141444444444444444444444444444444434343A39C
+      9CFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECCCC3C34848483F3F3F42
+      42424141414343434141414646468D8989EADEDEFBECECFBECECFBECECFBECEC
+      FBECECFBECECF1E3E3B2AAAA5F5D5D4242424141414E4E4ECAC1C1FBEDEDFBEC
+      ECF8EBEB605E5E4040404343434444444444444040404F4F4FACA5A5F2E5E5EE
+      E1E1CBC2C25D5C5C414141444444444444444444444444444444444444444444
+      4444444444444343434242424343434242424242424444444444444444444444
+      44444444434343474747C4BBBBFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFAECECA59F9F6665658884848F8A8A555454424242424242414141605E5E
+      ADA6A6C5BBBBCAC0C0C6BCBCBCB4B49A95956462624545454141414343434141
+      415A5A5AF4E7E7FBECECFBECECF4E7E75F5E5E42424244444444444444444444
+      44444141414545456766665D5C5C474747424242444444444444444444444444
+      4444444444444444444444444444444444444444444444444343434444444444
+      44444444444444444444444444444444434343454545B0A9A9FBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECF3E6E6FBECECFBECECABA4A4
+      4343434343434444444141414444444949494949494949494747474343434141
+      41404040424242444444434343474747C1B9B9FBECECFBECECEEE1E158585843
+      4343444444444444444444444444444444434343404040414141434343444444
+      4444444444444444444444444444444444444444444444444444444444444444
+      4444444444444444444444444444444444444444444444444444444444444443
+      43439A9595FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECECE0E0CFC5C5FBEDEDFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECD5CACA4C4C4C4343434444444242424343434141414242
+      424343434343434242424444445A59594F4F4F464646414141424242B2ABABFB
+      ECECFBECECFAEBEB807C7C424242444444444444444444444444444444444444
+      4444444444444444444444444444444444444444444444444444444444444444
+      4444444444444444444444444444444444444444444444444444444444444444
+      4444444444444444444444424242676565FAECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECBDB5B55A5959484848949090FAECECFBECEC
+      FBECECFBECECFBECECFBECECFBEDEDFBECECFBECECD1C5C54646464141414141
+      415B5A5A9A95957E7B7B464646434343444444444444A19B9BECE0E0E6DADAB2
+      AAAA6361618A8686EFE2E2FBECECFBECECFBECECC2B9B9464646434343444444
+      4444444444444444444444444444444444444444444444444444444444444444
+      4443434342424241414141414142424242424241414142424243434344444444
+      4444444444444444444444444444444444444444444444434343464646BEB6B6
+      FBECECFBECECFBECECF6E9E9BEB6B6A09A9ABEB6B6FBEDEDFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECEADEDE505050404040
+      424242454545B0A9A9FBECECFBECECFBECECFBECECE7DCDC7F7B7B9C9797DFD4
+      D4E3DADA7371714444446F6D6DE8DCDCFBECECFBECECA6A1A14343433F3F3F4A
+      4A4AE9DDDDFBECECFBECECFBECECF5E8E8FBECECFBECECFBECECFBECECFBECEC
+      EBDEDE4F4F4F4242424444444444444444444444444444444444444444444444
+      444444444343434141414343434646465050506D6A6A6E6C6C6E6C6C716F6F67
+      66664A4A4A474747454545424242424242434343444444444444444444444444
+      444444444444414141595858AAA3A3B7AFAF999393716F6F4646464141414545
+      45948F8FFBEEEEFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECEDE0E0F3E6E6FBECECFBECECFBECEC
+      FBECECC2B9B94747474343434444444141415E5D5DEADEDEFBECECFBECECEFE2
+      E26F6D6D404040414141535252C7C0C0F5E7E7CAC1C1EDE1E1FBECECFBECECFB
+      ECECF8EAEA787676464646868282F7E9E9FBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECF6E8E86A68684141414444444444444444444444
+      444444444444444444444242424141414747476D6B6B8F8A8AC6BDBDE2D6D6F5
+      E7E7F5E7E7F5E7E7F6E8E8F3E5E5D8CDCDCCC2C2ADA6A6817D7D5A5959424242
+      4242424444444444444444444444444444444444444242424444444646464343
+      43414141434343454545424242444444A19B9BFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECC1B9B9595858
+      6B6A6AE8DDDDFBECECFBECECFAEDED7673734141414444444444444444444444
+      44686666AAA3A3ADA6A66D6B6B414141434343424242464646BDB4B4FBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECF0E4E4D0C7C7F4E7E7FBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECEBDFDF6362624141
+      414444444444444444444444444444444343434242424B4B4B898585C2B9B9FB
+      EDEDFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECE8DCDC9F9A9A4A4A4A4242424242424444444444444444444444
+      444444444343434343434444444444444444444444444444444242424D4D4DDE
+      D3D3FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECC7BEBE4C4C4C3F3F3F404040615F5FA39C9C9E9898706D6D4545454343
+      4344444444444444444444444442424244444444444442424244444444444443
+      4343484848C5BCBCFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECF5E7E7FBECECFBECECF6E8E8E7DB
+      DBB2AAAA605E5E42424244444444444444444444444444444442424246464683
+      7F7FDED3D3FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECD8CDCD9893934949
+      4942424244444444444444444444444444444444444444444444444444444444
+      4444444444434343474747BFB6B6FBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECEC8E8A8A4040404444444444444141414444
+      4444444441414143434344444444444443434343434343434343434343434343
+      4343444444444444444444444444444444A9A2A2FBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECF7EAEA9F9A9A706D
+      6D9691917E7B7B716E6E53535344444442424244444444444444444444444444
+      44444141414C4C4CBAB3B3FBEDEDFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECCCC3C35B5A5A41414144444444444444444444444444
+      4444444444444444444444444444444444414141575757E9DDDDFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECD1C7C74E4E
+      4E43434344444444444444444444444445454542424241414143434346464648
+      48484747474545454242424141414242424444444444444444444141416A6868
+      F3E6E6FBECECFBECECD8CDCDEBDFDFFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECEC908C8C43434341414142424242424241414142424244444444444444
+      4444444444444444444444424242484848C1B9B9FBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECD9CECE57565641
+      4141444444444444444444444444444444444444444444444444434343454545
+      AFA8A8FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBEDED706E6E4242424444444444444444444343434141414C
+      4C4C7C7979928C8CBBB3B3C5BCBCC4BBBBABA5A58E8A8A6E6C6C4A4A4A434343
+      424242444444444444434343737171B7AFAF9C9797474747605E5EC6BDBDFBEC
+      ECFBECECFBECECFBECECFBECECD0C7C749494941414144444444444444444444
+      4444444444444444444444444444444444444444434343424242999494FBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECD9CECE5B5A5A414141444444444444444444444444444444
+      4444444444444242427E7B7BFBEDEDFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBEEEE69676741414144444444
+      4444424242464646868282D9CFCFFBEDEDFBECECFBECECFBECECFBECECFBECEC
+      FBECECF5E8E8D7CDCD8D89894F4F4F4242424444444343434141414646464343
+      434242423F3F3F515151E3D7D7FBECECFBECECFBECECFBECEC8C888841414144
+      4444444444444444444444444444444444444444444444444444444444444444
+      3F3F3F6D6B6BF4E7E7FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECD6CBCB535353414141
+      444444444444444444444444444444434343474747C6BCBCFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECE9DCDCC9BFBFD0C5C5C4BBBB86
+      8282434343444444444444404040565555BCB4B4FBEDEDFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECDACFCF7A77774242
+      42434343444444434343434343444444424242464646CDC3C3FBECECFBECECFB
+      ECECFBEDED797676404040444444444444444444444444444444444444444444
+      444444444444444444404040545454E0D5D5FBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECB4ACAC4343434343434444444444444444444444444343434A4A
+      4AD1C6C6FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECDFD4D463
+      61614747474949494949494242424343434444444040404E4E4ECFC6C6FBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECF7EAEA7976764242424444444444444444444343434444449D
+      9898F9EAEAFBECECFBECECFBECECFBECECA8A1A1444444414141444444444444
+      444444444444444444444444444444444444424242464646B6AFAFFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECF4E6E66E6B6B4141414444444444
+      444444444444444343434C4C4CD3C8C8FBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECF1E4E46B6969414141434343434343434343444444444444424242
+      444444B5AEAEFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECF1E4E467666640404044
+      4444444444434343515050E1D6D6FBECECFBECECFBECECFBECECFBECECFAECEC
+      9592924545454242424444444444444444444444444444444444444444444040
+      40908C8CFBEDEDFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECD7CDCD4B4B4B4242424444444444444444444343434B4B4BD6CBCBFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECE8DBDB4D4D4D404040444444444444
+      444444444444444444404040908C8CFBEDEDFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECD2C8C84B4B4B434343444444434343545353E1D5D5FBECECFBECEC
+      FBECECFBECECFBECECFBECECFBEDEDC2BABA5757574242424444444444444444
+      44444444444444414141636161EDE1E1FBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECEC7F7C7C41414144444444444444444443
+      4343444444A6A0A0FAECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      AAA4A4484848424242444444444444444444404040626161F2E6E6FBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECEC918C8C424242444444424242
+      515151E2D6D6FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECB8B1
+      B1444444444444444444444444444444444444434343A49E9EFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECC1B8B845
+      45454343434444444444444444444343434545457C7979A8A1A1A19A9AAEA7A7
+      F9ECECFBECECFBECECFBECECFBECECB5AEAE4B4A4A4343434444444343434B4B
+      4BC9C0C0FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      E2D7D7515050424242434343484848BCB4B4F6E8E8FBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECE8DBDB5757574141414444444444444444444242424A
+      4A4ADACFCFFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECF1E3E3696767414141444444444444444444444444434343
+      414141444444444444454545757373E3D7D7FBECECFBECECFBECECFBECEC8C88
+      88414141444444424242868282FBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECEC8884844242424444444343434E4E4E716E
+      6E807D7DCBC2C2FBECECFBECECFBECECFBECECFBECECF7E9E966646441414144
+      4444444444444444414141716E6EF8EAEAFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC928D8D424242444444
+      444444444444444444444444444444434343444444434343404040898585FBEC
+      ECFBECECFBECECFBECEC797676424242444444444444ACA5A5FBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECACA5A54545
+      45434343444444424242414141414141575656E7DBDBFBECECFBECECFBECECFB
+      ECECD4CBCB4C4C4C424242444444444444444444434343898484FBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECEC9A95954343434444444444444444444444444444444444444444444444
+      44444444424242545353FBECECFBECECFBECECDACFCF504F4F42424243434349
+      4949D5CACAFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECF7EAEBC6C4CA56616A52637152677656687654606A4E575D4C4D4EBA
+      B1B1FBECECFBECECFBECECEFE2E26D6B6B414141444444444444444444434343
+      454545ABA4A4FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECC4BCBC4848484343434444444444444444
+      44444444444444444444444444444444424242605E5EFBECECF2E4E4B5ADAD5B
+      5A5A4242424444444242424A4A4ADCD1D1FBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECD3DCEAA6CAEC80BAEA66AEEA4EA1E63390DB2E8FDE2F90DF2F
+      90E03491DD398FD63E87C571ABDAA7CAEBD3DDEAE0D5D4787575424242444444
+      444444444444444444434343444444B9B1B1FBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECD6CBCB4C4C
+      4C4343434444444444444444444444444444444444444444444444444141416B
+      6969F3E6E67E7B7B4343434242424444444444444242424A4A4ADCD1D1FBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECDDE0EA92C2EA53A6EB3099EC2A94EE2694ED2594ED26
+      93EE2694ED2795ED2895ED2795ED2694ED2593EE2694ED2794EC2A94ED2F97E8
+      3A8FD3497AA34E5A64444444444444444444444444434343464646B9B1B1FBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECDACFCF504F4F42424244444444444444444444444444444444
+      4444434343404040454545A6A0A0DAD3D35857573D3D3D444444444444444444
+      434343484848D4CACAFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECD2DBEB7BB6EB3097ED2894ED2694ED28
+      94EE2895ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2895ED2894ED2595EF2894EE3097E94785B94F62714444444444
+      44434343464646B9B1B1FBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECD9CECE4F4F4F42424244444444
+      4444444444444444444444414141454545595858A09B9BFBEDEDDBD3D3565656
+      3D3D3D434343444444444444444444444444B0A8A8FBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECEDE7EB81BAEB3398EC26
+      93EC2694ED2A96EC2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2B95ED2A95ED2594
+      EE2592EE3298EA4F86B353585B434343434343A59E9EFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECDB
+      CFCF5150504343434444444444444444444444444141414B4B4BB0AAAAEEE2E2
+      FBECECFBECECF8EBEB908B8B4C4C4C494949414141434343444444434343918C
+      8CFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECC1
+      D3EA49A2E92593EF2694ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2794ED2693ED3D94DA546D81444444AE
+      A7A7FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECDACFCF4E4E4E424242444444444444444444434343
+      484848C3BBBBFBECECFBECECFBECECFBECECFBECECFAEBEBDACFCFC6BCBC7A77
+      774343434444444141416B6969F6E9E9FBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBEDEB98C2EA2D96EE2394EF2896EE2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED28
+      95ED2594ED2F95EE4C7A9E8B8786FBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECC8BEBE484848434343
+      444444444444444444424242757373FBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECDFD5D54A4A4A424242434343474747C1B8B8FBECECFB
+      ECECFBECECFBECECFBECECFBECECF8EAEB7AB5EA2994ED2694ED2A95EC2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2994EE2995ED2693EE2994ED5490C2E9DDDCFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECEC959090424242444444444444444444444444444444AFA8A8FBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECE9DCDC51515142424244
+      44444141416F6D6DFAECECFBECECFBECECFBECECFBECECF9ECEB76B4EB2693ED
+      2894EE2996ED2995ED2995EE2895ED2895ED2895ED2895ED2995ED2A95ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995EE
+      2894EF2693ED72AFE3FAECEBFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECF4E6E66160604242424444444444444444444343
+      43454545B6AEAEFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECE9DCDC515151424242444444434343444444A8A2A2FBECECFBECECFBECEC
+      FBEDEB7EB6EA2693ED2994EC2995EE2A95ED2894EF2A94ED2996ED2995EC2A94
+      EC2996ED2895EC2495EE2995ED2A94EE2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2A95ED2994EE2793EE85B9EBFBECEBFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECDACFCF4A4A4A4242
+      424444444444444444444444444242428D8989FBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECC5BCBC474747434343444444444444424242
+      444444908C8CF3E6E6FBECEC96C3EA2894EC2795ED2995ED2995ED2592EE3E9F
+      ECB3D8F2BBDBF5B8DAF5B8DAF5BADBF5BCDBF361AEED2491EF2895ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A95ED2695
+      EE2A94EEA4C7EBFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECEC848080414141444444444444444444444444444444434343464646A2
+      9D9DF9ECECFBECECFBECECFBECECFBECECFBECECFBECECF2E5E56B6969414141
+      444444444444444444444444424242424242696868B3C5D72E97EC2694ED2A95
+      ED2996ED2995ED2793ED409FEEF6FAFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAB
+      D4F42995EE2895EC2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995EC2995ED2494EE3197ECCEDAEAFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECE4D9D954535342424244444444444444444444
+      4444444444444444424242444444767373E9DDDDFBECECFBECECFBECECFBECEC
+      FBECECE0D5D54949494242424444444343434242424242424444444343434C52
+      563A94D92592EF2A95ED2995ED2995ED2995ED2894EE2E97ECC5E0F6FFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFFFCBE4F83298EC2894ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2B95EE2393ED50
+      A6EBF2E9EBFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEDED84808042424244
+      4444444444444444444444444444444444444444444444434343404040696767
+      ECDFDFFBECECFBECECFBECECFBECECF6E9E97977774141414343434B4B4B6260
+      604B4B4B424242444444528AB62792ED2896ED2895EE2995ED2995ED2995ED29
+      95EC2795EC9DCDF3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F7FB43A0EC2894EE
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2A95EE2995EE2694ED97C2E9FBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECB6AFAF454545434343444444444444444444444444444444444444444444
+      444444444444414141505050E5D9D9FBECECFBECECFBECECFBECECFBECECEEE1
+      E18C88888F8B8BD1C7C7FAEDEDCDC4C45453534D5B663298EA2694ED2A95ED29
+      95ED2995ED2995ED2995ED2994EE2895EC75B9F1FFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFF63AFEF2594EE2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2694EE379BEADDE1EC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECD9CFCF4C4C4C414141444444444444444444444444
+      4444444444444444444444444444444444444040406A6868F7E9E9FBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECA9A3A344
+      7FB02693EE2995EC2995ED2995ED2995ED2995ED2A95ED2995ED2493EE4DA6ED
+      F5F9FDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8AC4F32693ED2995ED2A95ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2693ED89BDEBFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECF0E3E36C6A6A404040444444
+      4444444444444444444444444444444444444444444444444444444444444444
+      44979292FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECBBBFC53398EB2594ED2A95ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2594EF3197EBE1EFFAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB2D8
+      F52995EB2795EE2A95EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2594EE3796E6DCDAE0FBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECEBDFDF
+      726F6F4141414343434444444444444444444444444444444242424141414242
+      42414141424242404040666464F2E5E5FBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECEC85AFD42794ED2896ED2A95ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2895ED2A94EBB8DAF5FFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFDFEEFA3299EC2895ED2994EE2995ED2A95ED2995ED2A
+      95ED2995ED2995ED2995ED2995ED2995ED2995EE2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2895ED2995
+      EE2994ED5781A2C4BCBCFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECE1D6D66D6B6B4141414444444444444444444444444444444242
+      424040404D4D4D7C7979868282807C7C5D5C5C757272DDD1D1FBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      54A2E22794ED2895EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2794ED90C7F3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F9FD49A5EF2594EE2C
+      95EC2A95ED2694ED2895ED2895EF2995ED2A95ED2695ED2694ED2795EF2895EE
+      2895EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995EE2794ED3D89C7494949928D8DF7EAEAFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECEDE0E0A59E9E5251514141414444444444444444
+      44444444444444424242474747827F7FD6CCCCFBECECFBECECFBECECEEE1E1FB
+      EDEDFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECDDE0EB3399EB2794EE2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2694ED67B2EFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFEFEFE70B6EE2593EF2995EE2795ED2F97ED2D95EC2694EE2A95ED2694EE
+      2A95EC2F96ED2895ED2894EE2A94EE2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2794EE3297E64D
+      59614242426D6B6BD2C8C8FBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECF0E3E3ADA6A65C5A5A4343434141
+      41444444444444444444444444444444444444434343948F8FFBEDEDFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECB0CEED2A95ED2895ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2695ED43A1EEF4
+      F8FCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96C9F42894EE2693ED73B8EFE3EEF8
+      CAE3F54CA6EC2493EE2E96EDB4D7F5E2F1FA9ECEF02C95ED2895ED2A95ED2A95
+      ED2A95ED2A95ED2995EE2A95ED2995EE2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2895ED2C95ED4B6A854343434040404A4A4A8D8888ACA5A5D1C6C6
+      EEE1E1FBEDEDFBECECFBECECF9EBEBF2E5E5FAECECD5CACAADA6A69792926261
+      6145454541414143434344444444444444444444444444444444444443434348
+      4848D3C9C9FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      EC8BBEEB2794EC2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2794EE3197ECCFE7F7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBBDBF7
+      2B97ED2392EE83BFF4FFFFFFFFFFFF9ACCF32492EF2D96EDD9ECF9FFFFFFFDFE
+      FD4AA4EB2292EF2894EF2894EF2894F02794EF2795EF2794EE2994EE2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2794EE497CA5444444444444
+      4242424141414444444A4A4A5D5C5C7C79798581818783836D6A6A6160606A68
+      684B4B4B45454542424241414143434344444444444444444444444444444444
+      4444444444444444444444464646C5BDBDFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECEC6DB2EB2595EC2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2795ED2994EEAAD4F5FFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFE6F1FA6BB5ED56AAED8BC2F1FFFFFFFFFFFFD2E7F85EAE
+      ED57AAEDC5E1F4FFFFFFFFFFFF95CAEF57AAEC65B1EF65B1EE64B0ED67B1EE62
+      B0EE46A3EC2D96EB2494ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2794ED3D80B54444444444444444444444444343434343434242424141414242
+      4242424242424243434341414143434343434344444444444444444444444444
+      44444444444444444444444444444444444444444444444343439A9595FBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECF2E8EB5BAAEC2794EE29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995EC
+      2793EE88C1F3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFF
+      FEFFFFFFFFFFFFFFFFFFFFFFFEFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFF
+      FFFEFFFFFDFFFFFDFFFFFEFFFFFEF2F8FBB5D9F4409FEC2592EE2A95EE2995ED
+      2995ED2995ED2995ED2995ED2793EE3786C4484A4C4444444444444444444444
+      4444444444444444444444444444444444444444444444444444444444444444
+      4444444444444444444444444444444444444444444444444444444444444444
+      444444424242636262F0E3E3FBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECE6E4EC4CA2EA2593ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2895EE2594ED5AAAEFFAFCFDFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      DBECF93D9EED2493ED2995ED2995ED2995ED2995ED2995ED2494ED3991D8545B
+      6144444444444444444444444444444444444444444444444444444444444444
+      4444444444444444444444444444444444444444444444434343444444444444
+      444444444444444444444444444444444444454545AEA7A7FBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECE1E2EC46A1EB2794EE2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A95ED2594EE3D9DECEDF5
+      FBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFACD5F32995EC2795EE2994EE2995ED2995
+      ED2995ED2594EC3692DC545F6744444444444444444444444444444444444444
+      4444444444444444444444444444444444444444444444444444444444414141
+      4343434444444343434141414343434444444444444444444444444444444343
+      43A6A0A0FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECE1E1EB47A1EB
+      2794EE2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2A96EC2695ED2A97EDCEE6F8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F7FC43A1
+      EC2894EE2995ED2995ED2995ED2995ED2694ED348FDA505B6444444443434342
+      4242434343424242424242444444444444444444444444444444444444444444
+      4444444343434444446E6C6C8B8787AAA3A38B86867471714848484141414444
+      444444444343434040405B5A5AEADDDDFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECE0E1EC459FEB2794ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2A94EE2893EDA2D1F5FFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFBFDFEC4E1F7BDDEF7C2E0F7BFDFF7C0DFF7C0DFF7BFDFF7
+      C0DFF7BFDFF7BFDFF7C0DFF7C0DFF7C2DFF6F5F9FCFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFFF55ABEF2894ED2A96EC2995ED2995ED2995ED2593ED37
+      90DB535E66434343434343656464908B8B8D88885A5959414141434343444444
+      444444444444444444444444444444424242666464F3E6E6FBECECFBECECFBEC
+      ECF8EAEAC9C0C05D5C5C4141414141414343436C6A6AD5CBCBFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECE7E4EC4BA2EC2593EE2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED26
+      94ED77BAEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9FBFD57ABEB2C95ED2E97EC
+      2D97EC2D97EC2D97EC2D97EC2D97EC2D97EC2D97EC2D97EC2D97EC2D95ED58AB
+      ECE5F2F9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFF52AAEF2793EE2A95ED29
+      95ED2995ED2995ED2694ED388DD24B51564545459A9595F1E4E4FBECECFBECEC
+      E2D6D67F7C7C434343434343444444444444444444444444444444444444B1AA
+      AAFBECECFBECECFBECECFBECECFBECECFBECECE4D8D88F8A8A6F6D6DA29C9CF3
+      E6E6FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECF2E8
+      EC5CAAED2594ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2694EE4EA6EEFBFCFDFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFF84C0F22593EE2894EE2894EE2894EE2894EE2894EE2894EE2894EE2894
+      EE2894EE2793EF2894EE2694ED8DC7F0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+      FEFF51A9EE2793EE2A95ED2995ED2995ED2995ED2793EE3783C3454648807D7D
+      FBEDEDFBECECFBECECFBECECFBECECFBEEEE7370704141414444444444444444
+      44444444434343474747CBC2C2FBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECF6E7E7FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECEC72B3EB2694EE2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2795ED399BEDE0EFFA
+      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFADD5F52895ED2895ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2895ED2894EE7ABCF1FFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFFFFEFEFF51A9EE2793EE2A95ED2995ED2995ED2995ED
+      2795ED3E7EB35D5C5CE2D7D7FBECECFBECECFBECECFBECECFBECECFBECECC6BE
+      BE464646424242444444444444444444434343474747C8BFBFFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC8FBFEC2994ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2794EE2C96EBC3DFF6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4F0F9369B
+      ED2893EF2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2895EC7BBCF1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFEFF51A9EE2793EE
+      2A95ED2995ED2995ED2895ED2794ED7CA9D2E3D7D7FBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECEEE1E15A5A5A3F3F3F4040404040404040403F3F3F4F
+      4F4FDFD3D3FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECB4CFED2A95EC2895ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995EE2794EE63B2EDA6D3F4A0CFF59FCF
+      F59FCFF5A5D1F593C9F3349AEC2894EE2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2A95ED2B95ED2595ED7CBDF0FFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFEFEFF51A9EE2793EE2A95ED2995ED2995ED2895ED2B96ECBDD3ECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECCEC4C46968684C
+      4C4C5050504C4C4C696767CDC3C3FBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECE1E2EB359AEB2894ED2995ED2995ED
+      2995ED2995ED2995ED2593ED2694EE2794EE2794EE2794EE2794EE2795ED2695
+      ED2593EF2695ED2894ED2794ED2794ED2794EE2594EE2794EC2794EE2794EE27
+      94EE2794EE2794EE2794EE2794EE2794EE2794EE2995ED2393EF2C96EDB9DBF4
+      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFEFF51A9EE2793EE2A95ED2995ED2995
+      ED2794ED3B9CECE9E5ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECF2E6E6D6CDCDD3CCCCD9D0D0F2E5E5FBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      60ACEA2695ED2A95ED2995ED2995ED2795ED3099EB63B0F062B0F161AFF062B1
+      EF62B0F062B0F061B0F062B0F062B1EF62B1EF62B1EF61B1EF61B1EF61B1EF61
+      B2EF61B0F062B1EF62B0F062B0F062B0F062B0F062B0F062B0F062B0F062AFF0
+      60B0F05BACEEAAD1F2FFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE52AA
+      EE2793ED2994EE2995ED2A95EE2693ED6CB1EAFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECA5CAEB2694EF2995ED2995ED2895ED2694ED3198
+      EBC4E0F4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFEFF
+      FFFEFFFFFEFFFFFEFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFFFFEFFFFFEFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFEFEFE4FA7EF2493EE2A95EC2994EE2995EE2895EEAECEEAFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECEBE6EA3E9DEB2594
+      ED2A95ED2995ED2A96EC2493EE349BECD3E8F8FFFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDCECFA349AED2994EE2995ED2A
+      95EC2593EE439FECEDE7EBFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECEC8FBEEB2594ED2995ED2895ED2995ED2B95ED2593ED49A3EDE9
+      F4FBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C
+      BCEE2693ED2995ED2995ED2995ED2793ED96C1EBFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECE3E2EA399BEB2594ED2A95ED29
+      95ED2995ED2895EE2293ED62AFECFAFBFBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+      FFFFFFFFFFFBFCFD96C9F12895ED2895ED2995ED2A95ED2594ED3D9CEBE8E5EB
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECEC96C2E92693EC2696EE2A95ED2995ED2A95ED2894EE2493EE74B8EDC7E3F8
+      C5E1F7C5E1F7C5E1F7C5E1F7C5E1F7C5E1F7C5E1F7C5E1F7C5E1F7C5E1F7C5E1
+      F7C5E1F7C5E1F7C1DFF6E9F3FBFFFFFFFFFFFFDBEDF9BDDCF6C9E2F6FCFDFEFF
+      FFFFFBFCFEC9E2F8C3E0F7C6E2F7ADD4F45EAEEE2995ED2994EE2995EC2995EE
+      2994EE2993ED9DC6EBFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECF3E9EB55A7E82493EE2995ED2995ED2995ED
+      2A95EC2895EE2894EE2C97EE2B96EC2C96EB2C96EB2C96EB2C96EB2C96EB2C96
+      EB2C96EB2C96EB2C96EB2C96EB2C96EB2D96EB2A96EB88C2F2FFFFFFFFFFFF9B
+      CDF42E96EC3198EBD6E9F9FFFFFFF6FAFD54A9ED2996EC2D96EE2995EE2594EE
+      2895ED2995ED2895ED2A95ED2392EE59A9E9F5EAECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECCFDCE9
+      3197EB2693EE2995ED2A96EC2995ED2996EC2795ED2795EE2895EE2895EE2895
+      EE2895EE2895EE2895EE2895EE2895EE2895EE2895EE2895EE2895EE2995EE27
+      93EE4FAAECFEFEFCFFFFFFC7E2F72896ED2593ED9CCDF1FFFFFFFFFFFF77BAEF
+      2392EF2795EE2895ED2894EF2995ED2896EC2A96EC2593EE349AEDD8DEEAFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECA6C9E82894ED2694EE2995EE2995ED2995ED2895
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2895ED2894ED5BAEED7CBDF253A9EC2793EE2794ED
+      359AEB73B9F175B8F0389CED2694ED2895ED2995ED2995ED2995ED2995ED2595
+      EE2A95EDAFCCEBFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC8EBEE92694
+      EE2795EE2B95EE2996EC2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A94EE2895EE2492EF
+      2493EC2593ED2B96EC2A95ED2694EF2693ED2691EE2594EE2A95ED2995EC2995
+      ED2996EC2995EE2894ED2795ED95C1E9FBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECEC82B9EB2693EE2695EE2B95ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2A95ED2A95ED2A95ED2A95ED2A95ED2995ED2995ED2995ED2A95ED2996
+      EC2A95ED2995ED2995ED2995ED2A95EE2694EE2895EC8BBDEBFBECEBFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEDEB88BCE92C94ED26
+      94EE2895ED2A95ED2A95ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2795EE2A94ED2694EE2C95EC95
+      C0E8FBECEBFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECEBA3C9E83197EC2593ED2695ED2A95ED2995ED2995ED2995ED
+      2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2A95EC28
+      95ED2493EE3499ECADCAEAFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECCAD9E957A8E92893EC
+      2594ED2995EE2995EE2995EE2995ED2995ED2995ED2995ED2995ED2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED28
+      95EE2A95ED2995EC2594EE2893ED5BA9E8D3DAEAFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECF3E9EB93C1E93C9CEC2694ED2693EE2896ED2995EE2995ED2995
+      ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995EE2995ED2494EE2694ED3E9EEB9AC3EAF5E9EBFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECDFE1EB8DBEEB3A9B
+      EB2894ED2794EE2794EE2895ED2895ED2995ED2995ED2995ED2995ED2995ED29
+      95ED2995ED2995ED2995ED2895ED2895ED2794EE2694ED2895EC3D9DEB90C0EC
+      E1E2EBFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECE6E4EBA4C9EA60ACEA389BED2C95EC2994EC2694EC25
+      94EE2593EE2794EE2794EE2694ED2793ED2693EE2695EC2A95ED2B95ED3B9CEC
+      66AEEAABCBEAEAE5EAFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECDF
+      E1EBB7CFEC93C0EB72B3EB5FABEC4CA4EA45A0EB46A0ED46A0EE50A4EB60ADEA
+      73B5EB94C2EAB9D1EBE3E2EBFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBEDECF3E9ECE8E5EBE0E1EC
+      E1E0ECE0E1ECE8E5ECF4E9EBFBEDECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBEC
+      ECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFB
+      ECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECECFBECEC
+      FBECECFBECECFBECECFBECECFBECECFBECEC}
+    Transparent = True
+  end
+  object lblProcess: TLabel
+    Left = 133
+    Top = 17
+    Width = 389
+    Height = 31
+    AutoSize = False
+    Caption = 'lblProcess'
+    Color = clBtnFace
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clWindowText
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    Font.Style = []
+    ParentColor = False
+    ParentFont = False
+    WordWrap = True
+  end
+  object lblProcessActive: TLabel
+    Left = 133
+    Top = 55
+    Width = 389
+    Height = 42
+    AutoSize = False
+    Caption = 
+      'lblProcess lblProcess lblProcess lblProcess lblProcess lblProces' +
+      's lblProcess lblProcess lblProcess lblProcess lblProcess lblProc' +
+      'ess lblProcess lblProcess '
+    Color = clBtnFace
+    Font.Charset = DEFAULT_CHARSET
+    Font.Color = clWindowText
+    Font.Height = -16
+    Font.Name = 'Tahoma'
+    Font.Style = []
+    ParentColor = False
+    ParentFont = False
+    WordWrap = True
+  end
+  object pgProgress1: TProgressBar
+    Left = 133
+    Top = 97
+    Width = 389
+    Height = 17
+    Position = 25
+    TabOrder = 0
+  end
+  object bbCancel: TBitBtn
+    Left = 376
+    Top = 128
+    Width = 146
+    Height = 33
+    Kind = bkCancel
+    NumGlyphs = 2
+    TabOrder = 1
+    OnClick = bbCancelClick
+  end
+end

+ 184 - 0
src/gui-classic/UFRMSplash.pas

@@ -0,0 +1,184 @@
+unit UFRMSplash;
+
+{ Copyright (c) 2021 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+interface
+
+{$I ../config.inc}
+
+uses
+{$IFnDEF FPC}
+  pngimage, Windows, Messages,
+{$ELSE}
+  LCLIntf, LCLType, LMessages,
+{$ENDIF}
+  SysUtils, Variants, Classes, Graphics, Controls, Forms,
+  Dialogs, StdCtrls, UAccounts, Buttons, ActnList,
+  ExtCtrls, ComCtrls,
+  UNode, UWallet, UNetProtocol, UPCDataTypes, UThread,
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
+
+type
+  TSplashProcess = record
+    processName : String;
+    allowProgress : Boolean;
+    allowCancel : Boolean;
+    maxValue : Integer;
+    currentValue : Integer;
+  end;
+
+  ESplashCancel = Class(EAbort);
+
+  TFRMSplash = class(TForm)
+    imgSplash: TImage;
+    lblProcess: TLabel;
+    pgProgress1: TProgressBar;
+    bbCancel: TBitBtn;
+    lblProcessActive: TLabel;
+    procedure bbCancelClick(Sender: TObject);
+    procedure FormCreate(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
+  private
+    { Private declarations }
+    FSplashProcesses : TList<TSplashProcess>;
+    FDoCancel : Boolean;
+  public
+    { Public declarations }
+    class procedure SplashStart(const AOwnerForm : TForm; const AProcess : String; AAllowProgress, AAllowCancel : Boolean; AMaxValue : Integer; ACurrentValue : Integer);
+    class procedure SplashUpdate(const AProcess : String; ACurrentValue : Integer);
+    class procedure SplashFinalize;
+    class function SplashCount : Integer;
+    procedure UpdateInfo;
+  end;
+
+implementation
+
+{$R *.dfm}
+
+var _FRM : TFRMSplash;
+
+{ TFRMSplash }
+
+procedure TFRMSplash.bbCancelClick(Sender: TObject);
+begin
+  FDoCancel := True;
+end;
+
+procedure TFRMSplash.FormCreate(Sender: TObject);
+begin
+  FDoCancel := False;
+  lblProcess.Caption := '';
+  lblProcessActive.Caption := '';
+  pgProgress1.Visible := False;
+  bbCancel.Enabled := False;
+  bbCancel.Visible := False;
+  bbCancel.Cancel := True;
+  FSplashProcesses := TList<TSplashProcess>.Create;
+end;
+
+procedure TFRMSplash.FormDestroy(Sender: TObject);
+begin
+  FSplashProcesses.Free;
+end;
+
+class function TFRMSplash.SplashCount: Integer;
+begin
+  if Assigned(_FRM) then Result := _FRM.FSplashProcesses.Count
+  else Result := 0;
+end;
+
+class procedure TFRMSplash.SplashFinalize;
+begin
+  if Not Assigned(_FRM) then raise Exception.Create('No splash!');
+  if _FRM.FSplashProcesses.Count<=0 then raise Exception.Create('No splash count!');
+  _FRM.FSplashProcesses.Delete(_FRM.FSplashProcesses.Count-1);
+  if _FRM.FSplashProcesses.Count>0 then begin
+    _FRM.UpdateInfo;
+  end else begin
+    FreeAndNil(_FRM);
+  end;
+end;
+
+class procedure TFRMSplash.SplashStart(const AOwnerForm : TForm; const AProcess: String; AAllowProgress,
+  AAllowCancel: Boolean; AMaxValue, ACurrentValue: Integer);
+var Lprocess : TSplashProcess;
+begin
+  if Not Assigned(_FRM) then begin
+    _FRM := TFRMSplash.Create(AOwnerForm);
+    _FRM.bbCancel.Enabled := AAllowCancel;
+    _FRM.bbCancel.Visible := AAllowCancel;
+    _FRM.lblProcess.Caption := AProcess;
+  end;
+  Lprocess.processName := AProcess;
+  Lprocess.allowProgress := AAllowProgress;
+  Lprocess.allowCancel := AAllowCancel;
+  Lprocess.maxValue := AMaxValue;
+  Lprocess.currentValue := ACurrentValue;
+  _FRM.FSplashProcesses.Add(Lprocess);
+  _FRM.UpdateInfo;
+  _FRM.Show;
+  Application.ProcessMessages;
+end;
+
+class procedure TFRMSplash.SplashUpdate(const AProcess: String; ACurrentValue: Integer);
+var Lprocess : TSplashProcess;
+begin
+  if Not Assigned(_FRM) then raise Exception.Create('No splash!');
+  if _FRM.FSplashProcesses.Count<=0 then raise Exception.Create('No splash count!');
+  Lprocess := _FRM.FSplashProcesses.Items[_FRM.FSplashProcesses.Count-1];
+  if AProcess<>'' then Lprocess.processName := AProcess;
+  Lprocess.currentValue := ACurrentValue;
+  _FRM.FSplashProcesses.Items[_FRM.FSplashProcesses.Count-1] := Lprocess;
+  _FRM.UpdateInfo;
+end;
+
+procedure TFRMSplash.UpdateInfo;
+var Lprocess : TSplashProcess;
+begin
+  if FSplashProcesses.Count>0 then begin
+    Lprocess := _FRM.FSplashProcesses.Items[_FRM.FSplashProcesses.Count-1];
+
+  end else begin
+    Lprocess.processName := '';
+    Lprocess.allowProgress := false;
+    Lprocess.allowCancel := false;
+    Lprocess.maxValue := 100;
+    Lprocess.currentValue := 0;
+  end;
+  lblProcessActive.Caption := Lprocess.processName;
+  pgProgress1.Visible := Lprocess.allowProgress;
+  pgProgress1.Min := 0;
+  if Lprocess.maxValue>0 then
+    pgProgress1.Max := Lprocess.maxValue;
+  if (Lprocess.currentValue>=0) and (Lprocess.currentValue<=Lprocess.maxValue) then begin
+    pgProgress1.Position := Lprocess.maxValue;
+  end;
+  Application.ProcessMessages;
+  if FDoCancel then begin
+    raise ESplashCancel.Create('Canceled process: '+Lprocess.processName);
+  end;
+end;
+
+initialization
+  _FRM := Nil;
+finalization
+  FreeAndNil(_FRM);
+end.

+ 0 - 12
src/gui-classic/UFRMWallet.dfm

@@ -878,10 +878,6 @@ object FRMWallet: TFRMWallet
     object tsBlockChain: TTabSheet
       Caption = 'Block Explorer'
       ImageIndex = 1
-      ExplicitLeft = 0
-      ExplicitTop = 0
-      ExplicitWidth = 0
-      ExplicitHeight = 0
       object Panel2: TPanel
         Left = 0
         Top = 0
@@ -1061,10 +1057,6 @@ object FRMWallet: TFRMWallet
     object tsNodeStats: TTabSheet
       Caption = 'Node Stats'
       ImageIndex = 3
-      ExplicitLeft = 0
-      ExplicitTop = 0
-      ExplicitWidth = 0
-      ExplicitHeight = 0
       DesignSize = (
         857
         438)
@@ -1134,10 +1126,6 @@ object FRMWallet: TFRMWallet
     object tsMessages: TTabSheet
       Caption = 'Messages'
       ImageIndex = 6
-      ExplicitLeft = 0
-      ExplicitTop = 0
-      ExplicitWidth = 0
-      ExplicitHeight = 0
       DesignSize = (
         857
         438)

+ 20 - 19
src/gui-classic/UFRMWallet.lfm

@@ -4,7 +4,7 @@ object FRMWallet: TFRMWallet
   Top = 201
   Width = 865
   Caption = 'Pascal full node Wallet (Classic GUI)'
-  ClientHeight = 600
+  ClientHeight = 580
   ClientWidth = 865
   Color = clBtnFace
   Constraints.MinHeight = 600
@@ -16,6 +16,7 @@ object FRMWallet: TFRMWallet
   OnCreate = FormCreate
   OnDestroy = FormDestroy
   Position = poScreenCenter
+  LCLVersion = '2.0.10.0'
   object pnlTop: TPanel
     Left = 0
     Height = 91
@@ -311,7 +312,7 @@ object FRMWallet: TFRMWallet
   object StatusBar: TStatusBar
     Left = 0
     Height = 23
-    Top = 577
+    Top = 557
     Width = 865
     Panels = <    
       item
@@ -331,7 +332,7 @@ object FRMWallet: TFRMWallet
   end
   object PageControl: TPageControl
     Left = 0
-    Height = 486
+    Height = 466
     Top = 91
     Width = 865
     ActivePage = tsMyAccounts
@@ -341,11 +342,11 @@ object FRMWallet: TFRMWallet
     OnChange = PageControlChange
     object tsMyAccounts: TTabSheet
       Caption = 'Account Explorer'
-      ClientHeight = 460
+      ClientHeight = 440
       ClientWidth = 857
       object Splitter1: TSplitter
         Left = 400
-        Height = 394
+        Height = 374
         Top = 66
         Width = 5
       end
@@ -471,17 +472,17 @@ object FRMWallet: TFRMWallet
       end
       object pnlAccounts: TPanel
         Left = 0
-        Height = 394
+        Height = 374
         Top = 66
         Width = 400
         Align = alLeft
         BevelOuter = bvNone
-        ClientHeight = 394
+        ClientHeight = 374
         ClientWidth = 400
         TabOrder = 1
         object dgAccounts: TDrawGrid
           Left = 0
-          Height = 360
+          Height = 340
           Top = 0
           Width = 400
           Align = alClient
@@ -495,7 +496,7 @@ object FRMWallet: TFRMWallet
         object pnlAccountsInfo: TPanel
           Left = 0
           Height = 34
-          Top = 360
+          Top = 340
           Width = 400
           Align = alBottom
           BevelOuter = bvNone
@@ -576,7 +577,7 @@ object FRMWallet: TFRMWallet
       end
       object pcAccountsOptions: TPageControl
         Left = 405
-        Height = 394
+        Height = 374
         Top = 66
         Width = 452
         ActivePage = tsMultiSelectAccounts
@@ -603,12 +604,12 @@ object FRMWallet: TFRMWallet
         end
         object tsMultiSelectAccounts: TTabSheet
           Caption = 'Selected Accounts For Batch Operation'
-          ClientHeight = 368
+          ClientHeight = 348
           ClientWidth = 444
           ImageIndex = 1
           object dgSelectedAccounts: TDrawGrid
             Left = 41
-            Height = 311
+            Height = 291
             Top = 31
             Width = 320
             Align = alLeft
@@ -645,7 +646,7 @@ object FRMWallet: TFRMWallet
           object pnlSelectedAccountsBottom: TPanel
             Left = 0
             Height = 26
-            Top = 342
+            Top = 322
             Width = 444
             Align = alBottom
             BevelOuter = bvNone
@@ -687,12 +688,12 @@ object FRMWallet: TFRMWallet
           end
           object pnlSelectedAccountsLeft: TPanel
             Left = 0
-            Height = 311
+            Height = 291
             Top = 31
             Width = 41
             Align = alLeft
             BevelOuter = bvNone
-            ClientHeight = 311
+            ClientHeight = 291
             ClientWidth = 41
             TabOrder = 3
             object sbSelectedAccountsAdd: TSpeedButton
@@ -1191,12 +1192,12 @@ object FRMWallet: TFRMWallet
   end
   object TimerUpdateStatus: TTimer
     OnTimer = TimerUpdateStatusTimer
-    left = 32
-    top = 56
+    Left = 32
+    Top = 56
   end
   object MainMenu: TMainMenu
-    left = 165
-    top = 160
+    Left = 165
+    Top = 160
     object miProject: TMenuItem
       Caption = 'Project'
       object miPrivatekeys: TMenuItem

+ 117 - 151
src/gui-classic/UFRMWallet.pas

@@ -35,7 +35,7 @@ uses
   UNode, UGridUtils, UJSONFunctions, UAccounts, Menus, ImgList, UNetProtocol,
   UCrypto, Buttons, UPoolMining, URPC, UFRMAccountSelect, UConst,
   UAccountKeyStorage, UBaseTypes, UPCDataTypes, UOrderedList,
-  UFRMRPCCalls, UTxMultiOperation,
+  UFRMRPCCalls, UTxMultiOperation, USettings, UEPasa,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 Const
@@ -43,7 +43,6 @@ Const
   CM_PC_NetConnectionUpdated = WM_USER + 2;
 
 type
-  TMinerPrivateKey = (mpk_NewEachTime, mpk_Random, mpk_Selected);
 
   { TFRMWallet }
 
@@ -229,14 +228,12 @@ type
     procedure MiAccountInformationClick(Sender: TObject);
     procedure FormClose(Sender: TObject; var Action: TCloseAction);
     procedure Test_ShowDiagnosticTool(Sender: TObject);
+    procedure miAskForAccountClick(Sender: TObject);
   private
     FLastNodesCacheUpdatedTS : TDateTime;
     FBackgroundPanel : TPanel;
     FBackgroundLabel : TLabel;
     FMinersBlocksFound: Integer;
-    {$IFDEF TESTNET}
-    FLastAskForAccountTC : TTickCount;
-    {$ENDIF}
     procedure SetMinersBlocksFound(const Value: Integer);
     Procedure CheckIsReady;
     Procedure FinishedLoadingApp;
@@ -246,7 +243,6 @@ type
     Procedure InitMenuForTesting;
     {$IFDEF TESTNET}
     Procedure Test_RandomOperations(Sender: TObject);
-    Procedure Test_AskForFreeAccount(Sender: TObject);
     {$IFDEF TESTING_NO_POW_CHECK}
     Procedure Test_CreateABlock(Sender: TObject);
     {$ENDIF}
@@ -262,7 +258,6 @@ type
     FIsActivated : Boolean;
     FWalletKeys : TWalletKeysExt;
     FLog : TLog;
-    FAppParams : TAppParams;
     FNodeNotifyEvents : TNodeNotifyEvents;
     FAccountsGrid : TAccountsGrid;
     FSelectedAccountsGrid : TAccountsGrid;
@@ -270,7 +265,7 @@ type
     FPendingOperationsGrid : TOperationsGrid;
     FOperationsExplorerGrid : TOperationsGrid;
     FBlockChainGrid : TBlockChainGrid;
-    FMinerPrivateKeyType : TMinerPrivateKey;
+    FMinerPrivateKeyType : TMinerPrivateKeyType;
     FUpdating : Boolean;
     FMessagesUnreadCount : Integer;
     FMinAccountBalance : Int64;
@@ -298,9 +293,7 @@ type
     Procedure UpdateBlockChainState;
     Procedure UpdatePrivateKeys;
     Procedure UpdateOperations;
-    Procedure LoadAppParams;
-    Procedure SaveAppParams;
-    Procedure UpdateConfigChanged;
+    Procedure UpdateConfigChanged(Sender:TObject);
     Procedure UpdateNodeStatus;
     Procedure UpdateAvailableConnections;
     procedure Activate; override;
@@ -336,12 +329,13 @@ Uses UFolderHelper,{$IFDEF USE_GNUGETTEXT}gnugettext,{$ENDIF}
   UFRMOperationsExplorer,
   {$IFDEF TESTNET}
   UFRMRandomOperations,
-  UPCTNetDataExtraMessages,
   UFRMDiagnosticTool,
   {$ENDIF}
-  UAbstractBTree,
+  UPCTNetDataExtraMessages,
+  UFRMAskForAccount,
+  UAbstractBTree, UEPasaDecoder,
   UFRMAbout, UFRMOperation, UFRMWalletKeys, UFRMPayloadDecoder, UFRMNodesIp, UFRMMemoText,
-  USettings, UCommon, UPCOrderedLists;
+  UCommon, UPCOrderedLists;
 
 Type
 
@@ -441,19 +435,20 @@ begin
         Raise;
       end;
     End;
-    ips := FAppParams.ParamByName[CT_PARAM_TryToConnectOnlyWithThisFixedServers].GetAsString('');
+    ips := TSettings.TryConnectOnlyWithThisFixedServers;
     TNode.DecodeIpStringToNodeServerAddressArray(ips,nsarr);
     TNetData.NetData.DiscoverFixedServersOnly(nsarr);
     setlength(nsarr,0);
     // Creating Node:
     FNode := TNode.Node;
-    FNode.NetServer.Port := FAppParams.ParamByName[CT_PARAM_InternetServerPort].GetAsInteger(CT_NetServer_Port);
-    FNode.PeerCache := FAppParams.ParamByName[CT_PARAM_PeerCache].GetAsString('')+';'+CT_Discover_IPs;
+    FNode.NetServer.Port := TSettings.InternetServerPort;
+    FNode.PeerCache := TSettings.PeerCache+';'+CT_Discover_IPs;
+    FNode.MaxPayToKeyPurchasePrice := TSettings.MaxPayToKeyPurchasePrice;
     // Create RPC server
     FRPCServer := TRPCServer.Create;
     FRPCServer.WalletKeys := WalletKeys;
-    FRPCServer.Active := FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].GetAsBoolean(false);
-    FRPCServer.ValidIPs := FAppParams.ParamByName[CT_PARAM_JSONRPCAllowedIPs].GetAsString('127.0.0.1');
+    FRPCServer.Active := TSettings.JsonRpcPortEnabled;
+    FRPCServer.ValidIPs := TSettings.JsonRpcAllowedIPs;
     WalletKeys.SafeBox := FNode.Bank.SafeBox;
     // Check Database
     FNode.Bank.StorageClass := TFileStorage;
@@ -464,19 +459,17 @@ begin
     FWalletKeys.OnChanged.Add( OnWalletChanged );
     FAccountsGrid.Node := FNode;
     FOperationsAccountGrid.Node := FNode;
-    FBlockChainGrid.HashRateAverageBlocksCount := FAppParams.ParamByName[CT_PARAM_HashRateAvgBlocksCount].GetAsInteger(50);
-    i := FAppParams.ParamByName[CT_PARAM_ShowHashRateAs].GetAsInteger(Integer({$IFDEF TESTNET}hr_Mega{$ELSE}hr_Tera{$ENDIF}));
+    FBlockChainGrid.HashRateAverageBlocksCount := TSettings.HashRateAvgBlocksCount;
+    i := Integer(TSettings.ShowHashRateAs);
     if (i<Integer(Low(TShowHashRateAs))) Or (i>Integer(High(TShowHashRateAs))) then i := Integer({$IFDEF TESTNET}hr_Mega{$ELSE}hr_Tera{$ENDIF});
     FBlockChainGrid.HashRateAs := TShowHashRateAs(i);
     // Reading database
     FThreadActivate := TThreadActivate.Create(true);
     TThreadActivate(FThreadActivate).FreeOnTerminate := true;
     TThreadActivate(FThreadActivate).Suspended := False;
-    UpdateConfigChanged;
+    UpdateConfigChanged(Self);
     UpdateNodeStatus;
-    {$IFDEF TESTNET}
     TPCTNetDataExtraMessages.InitNetDataExtraMessages(FNode,TNetData.NetData,FWalletKeys);
-    {$ENDIF}
   Except
     On E:Exception do begin
       E.Message := 'An error occurred during initialization. Application cannot continue:'+#10+#10+E.Message+#10+#10+'Application will close...';
@@ -486,11 +479,11 @@ begin
   end;
   UpdatePrivateKeys;
   UpdateAccounts(false);
-  if FAppParams.ParamByName[CT_PARAM_FirstTime].GetAsBoolean(true) then begin
-    FAppParams.ParamByName[CT_PARAM_FirstTime].SetAsBoolean(false);
+  if TSettings.FirstTime then begin
+    TSettings.FirstTime := false;
     miAboutPascalCoinClick(Nil);
   end;
-
+  PageControlChange(Nil);
 end;
 
 procedure TFRMWallet.bbAccountsRefreshClick(Sender: TObject);
@@ -525,7 +518,7 @@ begin
     finally
       FSelectedAccountsGrid.UnlockAccountsList;
     end;
-    DefaultFee := FAppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
+    DefaultFee := TSettings.DefaultFee;
     WalletKeys := FWalletKeys;
     ShowModal;
   Finally
@@ -617,9 +610,6 @@ Var i : integer;
  sClientApp, sLastConnTime : String;
  strings, sNSC, sRS, sDisc : TStrings;
  hh,nn,ss,ms : Word;
- {$IFDEF TESTNET}
- LFoundAccounts : Integer;
- {$ENDIF}
 begin
   Try
     if Not TNetData.NetData.NetConnections.TryLockList(100,l) then exit;
@@ -692,25 +682,6 @@ begin
   Finally
     FMustProcessNetConnectionUpdated := false;
   End;
-  {$IFDEF TESTNET}
-  // TESTNET ONLY: Will automatic ask for get an account to other nodes if I have not enough
-  if TPlatform.GetElapsedMilliseconds(FLastAskForAccountTC)<60000 then Exit; // 1 per minute
-  FLastAskForAccountTC := TPlatform.GetTickCount;
-  LFoundAccounts := 0;
-  FNode.Bank.SafeBox.StartThreadSafe;
-  try
-    for i := 0 to FWalletKeys.AccountsKeyList.Count-1 do begin
-      inc(LFoundAccounts,FWalletKeys.AccountsKeyList.AccountKeyList[i].Count);
-      if LFoundAccounts>5 then Break;
-    end;
-  finally
-    FNode.Bank.SafeBox.EndThreadSave;
-  end;
-  if LFoundAccounts<5 then begin
-    // Will only ask if I have less than 5 accounts
-    TPCTNetDataExtraMessages.AskForFreeAccount(GetAccountKeyForMiner);
-  end;
-  {$ENDIF}
 end;
 
 procedure TFRMWallet.CM_WalletChanged(var Msg: TMessage);
@@ -726,13 +697,13 @@ end;
 
 procedure TFRMWallet.dgAccountsColumnMoved(Sender: TObject; FromIndex, ToIndex: Integer);
 begin
-  SaveAppParams;
+  TSettings.Save;
 end;
 
 procedure TFRMWallet.dgAccountsFixedCellClick(Sender: TObject; ACol,
   ARow: Integer);
 begin
-  SaveAppParams;
+  TSettings.Save;
 end;
 
 procedure TFRMWallet.DoUpdateAccounts;
@@ -877,6 +848,8 @@ end;
 
 procedure TFRMWallet.FinishedLoadingApp;
 var LLockedMempool : TPCOperationsComp;
+  LFoundAccounts, i, LOnSafebox,LOnMempool : Integer;
+  Lpubkeys : TList<TAccountKey>;
 begin
   FNodeNotifyEvents.Node := FNode;
   // Init
@@ -890,16 +863,16 @@ begin
   TimerUpdateStatus.Enabled := true;
   //
   FPoolMiningServer := TPoolMiningServer.Create;
-  FPoolMiningServer.Port := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerPort].GetAsInteger(CT_JSONRPCMinerServer_Port);
+  FPoolMiningServer.Port := TSettings.JsonRpcMinerServerPort;
   FPoolMiningServer.MinerAccountKey := GetAccountKeyForMiner;
-  FPoolMiningServer.MinerPayload := TEncoding.ANSI.GetBytes( FAppParams.ParamByName[CT_PARAM_MinerName].GetAsString('') );
+  FPoolMiningServer.MinerPayload := TEncoding.ANSI.GetBytes(TSettings.MinerName);
   LLockedMempool := FNode.LockMempoolWrite;
   try
     LLockedMempool.AccountKey := GetAccountKeyForMiner;
   finally
     FNode.UnlockMempoolWrite;
   end;
-  FPoolMiningServer.Active := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].GetAsBoolean(true);
+  FPoolMiningServer.Active := TSettings.JsonRpcMinerServerActive;
   FPoolMiningServer.OnMiningServerNewBlockFound := OnMiningServerNewBlockFound;
   FreeAndNil(FBackgroundLabel);
   FreeAndNil(FBackgroundPanel);
@@ -907,9 +880,31 @@ begin
   PageControl.Visible:=True;
   PageControl.Enabled:=True;
 
-
-
   UpdatePrivateKeys;
+  //
+  LFoundAccounts := 0;
+  FNode.Bank.SafeBox.StartThreadSafe;
+  try
+    Lpubkeys := TList<TAccountKey>.Create;
+    Try
+      for i := 0 to FWalletKeys.Count-1 do begin
+        if (FWalletKeys.Key[i].HasPrivateKey) then begin
+          Lpubkeys.Add(FWalletKeys.Key[i].AccountKey);
+        end;
+      end;
+      if (Lpubkeys.Count>0) then begin
+        LFoundAccounts := FNode.GetAccountsAvailableByPublicKey(Lpubkeys,LOnSafebox,LOnMempool);
+      end else LFoundAccounts := 0;
+    Finally
+      Lpubkeys.Free;
+    End;
+  finally
+    FNode.Bank.SafeBox.EndThreadSave;
+  end;
+  if LFoundAccounts<1 then begin
+    // Will only ask if no accounts
+    TFRMAskForAccount.AskForAccount(Self,FNode,TNetData.NetData,FWalletKeys,GetAccountKeyForMiner);
+  end;
 end;
 
 procedure TFRMWallet.FillAccountInformation(const Strings: TStrings;
@@ -987,6 +982,7 @@ procedure TFRMWallet.FillOperationInformation(const Strings: TStrings;
   const OperationResume: TOperationResume);
 var i : Integer;
   jsonObj : TPCJSONObject;
+  LEPASA : TEPasa;
 begin
   If (not OperationResume.valid) then exit;
   If OperationResume.Block<FNode.Bank.BlocksCount then
@@ -1015,7 +1011,10 @@ begin
   If (Length(OperationResume.OperationHash_OLD)>0) then begin
     Strings.Add(Format('Old Operation Hash (old_ophash): %s',[TCrypto.ToHexaString(OperationResume.OperationHash_OLD)]));
   end;
-  Strings.Add(Format('Payload type:%d length:%d',[OperationResume.OriginalPayload.payload_type, length(OperationResume.OriginalPayload.payload_raw)]));
+  if TEPasaDecoder.TryDecodeEPASA(OperationResume.DestAccount,OperationResume.OriginalPayload,FNode,FWalletKeys,Nil,LEPASA) then begin
+    Strings.Add('EPASA: '+LEPASA.ToString);
+  end else Strings.Add('No EPASA format');
+  Strings.Add(Format('Payload type:%s length:%d',['0x'+IntToHex(OperationResume.OriginalPayload.payload_type,2), length(OperationResume.OriginalPayload.payload_raw)]));
   if (Length(OperationResume.OriginalPayload.payload_raw)>0) then begin
     If OperationResume.PrintablePayload<>'' then begin
       Strings.Add(Format('Payload (human): %s',[OperationResume.PrintablePayload]));
@@ -1027,7 +1026,9 @@ begin
   end;
   jsonObj := TPCJSONObject.Create;
   Try
-    TPascalCoinJSONComp.FillOperationObject(OperationResume,FNode.Bank.BlocksCount,jsonObj);
+    TPascalCoinJSONComp.FillOperationObject(OperationResume,FNode.Bank.BlocksCount,
+      FNode,FWalletKeys,Nil,
+      jsonObj);
     Strings.Add('OPERATION JSON:');
     Strings.Add(jsonObj.ToJSON(False));
   Finally
@@ -1093,13 +1094,17 @@ begin
   mi := TMenuItem.Create(MainMenu);
   mi.Caption:='Create a block';
   mi.OnClick:=Test_CreateABlock;
+  {$IFnDEF FPC}
   mi.ShortCut := TextToShortCut('CTRL+B');
+  {$ENDIF}
   miAbout.Add(mi);
   {$ENDIF}
   mi := TMenuItem.Create(MainMenu);
   mi.Caption:='Connect/Disconnect';
   mi.OnClick:=Test_ConnectDisconnect;
+  {$IFnDEF FPC}
   mi.ShortCut := TextToShortCut('CTRL+D');
+  {$ENDIF}
   miAbout.Add(mi);
 
   mi := TMenuItem.Create(MainMenu);
@@ -1107,10 +1112,6 @@ begin
   mi.OnClick:=Test_RandomOperations;
   miAbout.Add(mi);
   mi := TMenuItem.Create(MainMenu);
-  mi.Caption:='Ask for Free Account';
-  mi.OnClick:=Test_AskForFreeAccount;
-  miAbout.Add(mi);
-  mi := TMenuItem.Create(MainMenu);
   mi.Caption:='Diagnostic Tool';
   mi.OnClick:=Test_ShowDiagnosticTool;
   miAbout.Add(mi);
@@ -1126,7 +1127,15 @@ begin
   mi.Caption:='Search accounts for private or swap to me';
   mi.OnClick:=Test_FindAccountsForPrivateBuyOrSwapToMe;
   miAbout.Add(mi);
+{$ELSE}
 {$ENDIF}
+  mi := TMenuItem.Create(MainMenu);
+  mi.Caption:='-';
+  MiOperations.Add(mi);
+  mi := TMenuItem.Create(MainMenu);
+  mi.Caption:='Ask for Free Account';
+  mi.OnClick:=miAskForAccountClick;
+  MiOperations.Add(mi);
 end;
 
 {$IFDEF TESTING_NO_POW_CHECK}
@@ -1185,14 +1194,6 @@ begin
   end;
 end;
 
-Procedure TFRMWallet.Test_AskForFreeAccount(Sender: TObject);
-var i : Integer;
-begin
-  i := TPCTNetDataExtraMessages.AskForFreeAccount(GetAccountKeyForMiner);
-  if i>0 then ShowMessage(Format('Asked for a free account to %d nodes...',[i]))
-  else ShowMessage('Sorry. No nodes available to ask for a free account');
-end;
-
 {$ENDIF}
 
 procedure TFRMWallet.Test_ShowOperationsInMemory(Sender: TObject);
@@ -1306,9 +1307,6 @@ begin
   System.ReportMemoryLeaksOnShutdown := True; // Delphi memory leaks testing
   {$ENDIF}
   {$ENDIF}
-  {$IFDEF TESTNET}
-  FLastAskForAccountTC := 0;
-  {$ENDIF}
   FLastAccountsGridInvalidateTC := TPlatform.GetTickCount;
   FLastNodesCacheUpdatedTS := Now;
   FBackgroundPanel := Nil;
@@ -1339,8 +1337,8 @@ begin
   FLog.OnNewLog := OnNewLog;
   FLog.SaveTypes := [];
   If Not ForceDirectories(TNode.GetPascalCoinDataFolder) then raise Exception.Create('Cannot create dir: '+TNode.GetPascalCoinDataFolder);
-  FAppParams := TAppParams.Create(self);
-  FAppParams.FileName := TNode.GetPascalCoinDataFolder+PathDelim+'AppParams.prm';
+  TSettings.Load;
+  TSettings.OnChanged.Add(UpdateConfigChanged);
   FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
   FNodeNotifyEvents.OnBlocksChanged := OnNewAccount;
   FNodeNotifyEvents.OnNodeMessageEvent := OnNodeMessageEvent;
@@ -1357,18 +1355,21 @@ begin
   FOperationsAccountGrid := TOperationsGrid.Create(Self);
   FOperationsAccountGrid.DrawGrid := dgAccountOperations;
   FOperationsAccountGrid.MustShowAlwaysAnAccount := true;
+  FOperationsAccountGrid.WalletKeys := FWalletKeys;
   FPendingOperationsGrid := TOperationsGrid.Create(Self);
   FPendingOperationsGrid.DrawGrid := dgPendingOperations;
   FPendingOperationsGrid.AccountNumber := -1; // all
   FPendingOperationsGrid.PendingOperations := true;
+  FPendingOperationsGrid.WalletKeys := FWalletKeys;
   FOperationsExplorerGrid := TOperationsGrid.Create(Self);
   FOperationsExplorerGrid.DrawGrid := dgOperationsExplorer;
   FOperationsExplorerGrid.AccountNumber := -1;
   FOperationsExplorerGrid.PendingOperations := False;
+  FOperationsExplorerGrid.WalletKeys := FWalletKeys;
   FBlockChainGrid := TBlockChainGrid.Create(Self);
   FBlockChainGrid.DrawGrid := dgBlockChainExplorer;
+  FBlockChainGrid.ShowTimeAverageColumns:={$IFDEF SHOW_AVERAGE_TIME_STATS}True;{$ELSE}False;{$ENDIF}
   // FWalletKeys.OnChanged.Add( OnWalletChanged );
-  LoadAppParams;
   {$IFDEF USE_GNUGETTEXT}
   // use language from the params and retranslate if needed
   // might be better to move this a bit earlier in the formcreate routine
@@ -1379,7 +1380,7 @@ begin
   UpdatePrivateKeys;
   UpdateBlockChainState;
   UpdateConnectionStatus;
-  PageControl.ActivePage := tsMyAccounts;
+  PageControl.ActivePage := tsOperations;
   pcAccountsOptions.ActivePage := tsAccountOperations;
   ebFilterOperationsStartBlock.Text := '';
   ebFilterOperationsEndBlock.Text := '';
@@ -1434,7 +1435,7 @@ begin
   Try
     i := StrToIntDef(ebHashRateBackBlocks.Text,-1);
     FBlockChainGrid.HashRateAverageBlocksCount:=i;
-    FAppParams.ParamByName[CT_PARAM_HashRateAvgBlocksCount].Value := FBlockChainGrid.HashRateAverageBlocksCount;
+    TSettings.HashRateAvgBlocksCount := FBlockChainGrid.HashRateAverageBlocksCount;
   Finally
     ebHashRateBackBlocks.Text := IntToStr(FBlockChainGrid.HashRateAverageBlocksCount);
     FUpdating := false;
@@ -1456,7 +1457,7 @@ begin
       6 : FBlockChainGrid.HashRateAs := hr_Exa;
     else FBlockChainGrid.HashRateAs := hr_Mega;
     end;
-    FAppParams.ParamByName[CT_PARAM_ShowHashRateAs].Value := Integer(FBlockChainGrid.HashRateAs);
+    TSettings.ShowHashRateAs := FBlockChainGrid.HashRateAs;
   Finally
     FUpdating := false;
   End;
@@ -1476,8 +1477,7 @@ begin
   FreeAndNil(FRPCServer);
   FreeAndNil(FPoolMiningServer);
   step := 'Saving params';
-  SaveAppParams;
-  FreeAndNil(FAppParams);
+  TSettings.Save;
   //
   step := 'Assigning nil events';
   FLog.OnNewLog :=Nil;
@@ -1577,11 +1577,10 @@ Var PK : TECPrivateKey;
 begin
   Result := CT_TECDSA_Public_Nul;
   if Not Assigned(FWalletKeys) then exit;
-  if Not Assigned(FAppParams) then exit;
   case FMinerPrivateKeyType of
     mpk_NewEachTime: PublicK := CT_TECDSA_Public_Nul;
     mpk_Selected: begin
-      PublicK := TAccountComp.RawString2Accountkey(FAppParams.ParamByName[CT_PARAM_MinerPrivateKeySelectedPublicKey].GetAsTBytes(Nil));
+      PublicK := TAccountComp.RawString2Accountkey(TSettings.MinerSelectedPublicKey);
     end;
   else
     // Random
@@ -1608,9 +1607,9 @@ begin
       PublicK := PK.PublicKey;
       // Set to AppParams if not mpk_NewEachTime
       if (FMinerPrivateKeyType<>mpk_NewEachTime) then begin
-        FAppParams.ParamByName[CT_PARAM_MinerPrivateKeySelectedPublicKey].SetAsTBytes(TAccountComp.AccountKey2RawString(PublicK));
+        TSettings.MinerSelectedPublicKey := TAccountComp.AccountKey2RawString(PublicK);
         FMinerPrivateKeyType:=mpk_Selected;
-        FAppParams.ParamByName[CT_PARAM_MinerPrivateKeyType].Value := Integer(mpk_Selected);
+        TSettings.MinerPrivateKeyType := mpk_Selected;
       end;
     finally
       PK.Free;
@@ -1624,7 +1623,7 @@ Var FRM : TFRMNodesIp;
 begin
   FRM := TFRMNodesIp.Create(Self);
   Try
-    FRM.AppParams := FAppParams;
+    FRM.AppParams := TSettings.AppParams;
     FRM.ShowModal;
   Finally
     FRM.Free;
@@ -1636,28 +1635,6 @@ begin
   PageControl.ActivePage := tsMessages;
 end;
 
-procedure TFRMWallet.LoadAppParams;
-Var ms : TMemoryStream;
-  s : AnsiString;
-begin
-  ms := TMemoryStream.Create;
-  Try
-    s := FAppParams.ParamByName[CT_PARAM_GridAccountsStream].GetAsString('');
-    ms.WriteBuffer(s[1],length(s));
-    ms.Position := 0;
-    // Disabled on V2: FAccountsGrid.LoadFromStream(ms);
-  Finally
-    ms.Free;
-  End;
-  If FAppParams.FindParam(CT_PARAM_MinerName)=Nil then begin
-    // New configuration... assigning a new random value
-    FAppParams.ParamByName[CT_PARAM_MinerName].SetAsString('New Node '+DateTimeToStr(Now)+' - '+
-      CT_ClientAppVersion);
-  end;
-  FBlockChainGrid.ShowTimeAverageColumns:={$IFDEF SHOW_AVERAGE_TIME_STATS}True;{$ELSE}False;{$ENDIF}
-  UpdateConfigChanged;
-end;
-
 procedure TFRMWallet.miAboutPascalCoinClick(Sender: TObject);
 begin
   With TFRMAbout.Create(Self) do
@@ -1731,6 +1708,11 @@ begin
   sbSelectedAccountsAddClick(Sender);
 end;
 
+procedure TFRMWallet.miAskForAccountClick(Sender: TObject);
+begin
+  TFRMAskForAccount.AskForAccount(Self,FNode,TNetData.NetData,FWalletKeys,GetAccountKeyForMiner);
+end;
+
 procedure TFRMWallet.MiCloseClick(Sender: TObject);
 begin
   Close;
@@ -1739,11 +1721,11 @@ end;
 procedure TFRMWallet.MiDecodePayloadClick(Sender: TObject);
 begin
   if PageControl.ActivePage=tsOperations then begin
-    FOperationsExplorerGrid.ShowModalDecoder(FWalletKeys,FAppParams);
+    FOperationsExplorerGrid.ShowModalDecoder(FWalletKeys, TSettings.AppParams);
   end else if PageControl.ActivePage=tsPendingOperations then begin
-    FPendingOperationsGrid.ShowModalDecoder(FWalletKeys,FAppParams);
+    FPendingOperationsGrid.ShowModalDecoder(FWalletKeys,TSettings.AppParams);
   end else if PageControl.ActivePage=tsMyAccounts then begin
-    FOperationsAccountGrid.ShowModalDecoder(FWalletKeys,FAppParams);
+    FOperationsAccountGrid.ShowModalDecoder(FWalletKeys,TSettings.AppParams);
   end;
 end;
 
@@ -1989,7 +1971,7 @@ begin
   //
   FRM := TFRMPayloadDecoder.Create(Self);
   try
-    FRM.Init(CT_TOperationResume_NUL,WalletKeys,FAppParams);
+    FRM.Init(CT_TOperationResume_NUL,WalletKeys,TSettings.AppParams);
     FRM.DoFind(oph);
     FRM.ShowModal;
   finally
@@ -2038,7 +2020,7 @@ begin
     finally
       l.Free;
     end;
-    DefaultFee := FAppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
+    DefaultFee := TSettings.DefaultFee;
     WalletKeys := FWalletKeys;
     ShowModal;
   Finally
@@ -2050,11 +2032,11 @@ procedure TFRMWallet.miOptionsClick(Sender: TObject);
 begin
   With TFRMPascalCoinWalletConfig.Create(Self) do
   try
-    AppParams := Self.FAppParams;
+    AppParams := TSettings.AppParams;
     WalletKeys := Self.FWalletKeys;
     if ShowModal=MrOk then begin
-      SaveAppParams;
-      UpdateConfigChanged;
+      TSettings.Save;
+      UpdateConfigChanged(Self);
       {$IFDEF USE_GNUGETTEXT}RetranslateComponent(self);{$ENDIF}
     end;
   finally
@@ -2253,7 +2235,7 @@ begin
     s := DateTimeToStr(now)+' Message received from '+NetConnection.ClientRemoteAddr;
     memoMessages.Lines.Add(DateTimeToStr(now)+' Message received from '+NetConnection.ClientRemoteAddr+' Length '+inttostr(Length(MessageData))+' bytes');
     memoMessages.Lines.Add('RECEIVED> '+MessageData);
-    if FAppParams.ParamByName[CT_PARAM_ShowModalMessages].GetAsBoolean(false) then begin
+    if TSettings.ShowModalMessages then begin
       s := DateTimeToStr(now)+' Message from '+NetConnection.ClientRemoteAddr+#10+
          'Length '+inttostr(length(MessageData))+' bytes'+#10+#10;
       if TCrypto.IsHumanReadable(TEncoding.ANSI.GetBytes(MessageData)) then begin
@@ -2291,7 +2273,7 @@ begin
     if (s<>'') then s := s+';';
     s := s + nsarr[i].ip+':'+IntToStr( nsarr[i].port );
   end;
-  FAppParams.ParamByName[CT_PARAM_PeerCache].SetAsString(s);
+  TSettings.PeerCache := s;
   TNode.Node.PeerCache := s;
 end;
 
@@ -2336,22 +2318,6 @@ begin
   end;
 end;
 
-procedure TFRMWallet.SaveAppParams;
-Var ms : TMemoryStream;
-  s : AnsiString;
-begin
-  ms := TMemoryStream.Create;
-  Try
-    FAccountsGrid.SaveToStream(ms);
-    ms.Position := 0;
-    setlength(s,ms.Size);
-    ms.ReadBuffer(s[1],ms.Size);
-    FAppParams.ParamByName[CT_PARAM_GridAccountsStream].SetAsString(s);
-  Finally
-    ms.Free;
-  End;
-end;
-
 procedure TFRMWallet.sbSelectedAccountsAddAllClick(Sender: TObject);
 Var lsource,ltarget : TOrderedCardinalList;
   i : Integer;
@@ -2589,18 +2555,18 @@ begin
   end;
 end;
 
-procedure TFRMWallet.UpdateConfigChanged;
+procedure TFRMWallet.UpdateConfigChanged(Sender:TObject);
 Var wa : Boolean;
   i : Integer;
   LLockedMempool : TPCOperationsComp;
 begin
-  tsLogs.TabVisible := FAppParams.ParamByName[CT_PARAM_ShowLogs].GetAsBoolean(false);
+  tsLogs.TabVisible := TSettings.ShowLogs;
   if (Not tsLogs.TabVisible) then begin
     FLog.OnNewLog := Nil;
     if PageControl.ActivePage = tsLogs then PageControl.ActivePage := tsMyAccounts;
   end else FLog.OnNewLog := OnNewLog;
-  if (FAppParams.ParamByName[CT_PARAM_SaveLogFiles].GetAsBoolean(false)) then begin
-    if FAppParams.ParamByName[CT_PARAM_SaveDebugLogs].GetAsBoolean(false) then FLog.SaveTypes := CT_TLogTypes_ALL
+  if TSettings.SaveLogFiles then begin
+    if TSettings.SaveDebugLogs then FLog.SaveTypes := CT_TLogTypes_ALL
     else FLog.SaveTypes := CT_TLogTypes_DEFAULT;
     FLog.FileName := TNode.GetPascalCoinDataFolder+PathDelim+'PascalCointWallet.log';
   end else begin
@@ -2609,30 +2575,30 @@ begin
   end;
   if Assigned(FNode) then begin
     wa := FNode.NetServer.Active;
-    FNode.NetServer.Port := FAppParams.ParamByName[CT_PARAM_InternetServerPort].GetAsInteger(CT_NetServer_Port);
+    FNode.NetServer.Port := TSettings.InternetServerPort;
     FNode.NetServer.Active := wa;
     LLockedMempool := FNode.LockMempoolWrite;
     try
-      LLockedMempool.BlockPayload := TEncoding.ANSI.GetBytes(FAppParams.ParamByName[CT_PARAM_MinerName].GetAsString(''));
+      LLockedMempool.BlockPayload := TEncoding.ANSI.GetBytes(TSettings.MinerName);
     finally
       FNode.UnlockMempoolWrite;
     end;
     FNode.NodeLogFilename := TNode.GetPascalCoinDataFolder+PathDelim+'blocks.log';
   end;
   if Assigned(FPoolMiningServer) then begin
-    if FPoolMiningServer.Port<>FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerPort].GetAsInteger(CT_JSONRPCMinerServer_Port) then begin
+    if FPoolMiningServer.Port<>TSettings.JsonRpcMinerServerPort then begin
       FPoolMiningServer.Active := false;
-      FPoolMiningServer.Port := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerPort].GetAsInteger(CT_JSONRPCMinerServer_Port);
+      FPoolMiningServer.Port := TSettings.JsonRpcMinerServerPort;
     end;
-    FPoolMiningServer.Active :=FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].GetAsBoolean(true);
-    FPoolMiningServer.UpdateAccountAndPayload(GetAccountKeyForMiner,TEncoding.ANSI.GetBytes(FAppParams.ParamByName[CT_PARAM_MinerName].GetAsString('')));
+    FPoolMiningServer.Active := TSettings.JsonRpcMinerServerActive;
+    FPoolMiningServer.UpdateAccountAndPayload(GetAccountKeyForMiner,TEncoding.ANSI.GetBytes(TSettings.MinerName));
   end;
   if Assigned(FRPCServer) then begin
-    FRPCServer.Active := FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].GetAsBoolean(false);
-    FRPCServer.ValidIPs := FAppParams.ParamByName[CT_PARAM_JSONRPCAllowedIPs].GetAsString('127.0.0.1');
+    FRPCServer.Active := TSettings.JsonRpcPortEnabled;
+    FRPCServer.ValidIPs := TSettings.JsonRpcAllowedIPs;
   end;
-  i := FAppParams.ParamByName[CT_PARAM_MinerPrivateKeyType].GetAsInteger(Integer(mpk_Random));
-  if (i>=Integer(Low(TMinerPrivatekey))) And (i<=Integer(High(TMinerPrivatekey))) then FMinerPrivateKeyType := TMinerPrivateKey(i)
+  i := Integer(TSettings.MinerPrivateKeyType);
+  if (i>=Integer(Low(TMinerPrivateKeyType))) And (i<=Integer(High(TMinerPrivateKeyType))) then FMinerPrivateKeyType := TMinerPrivateKeyType(i)
   else FMinerPrivateKeyType := mpk_Random;
   ebHashRateBackBlocks.Text := IntToStr(FBlockChainGrid.HashRateAverageBlocksCount);
   Case FBlockChainGrid.HashRateAs of
@@ -2646,8 +2612,8 @@ begin
   else cbHashRateUnits.ItemIndex:=-1;
   end;
   if TNetData.NetDataExists then begin
-    if FAppParams.ParamByName[CT_PARAM_AllowDownloadNewCheckpointIfOlderThan].GetAsBoolean(TNetData.NetData.MinFutureBlocksToDownloadNewSafebox>200) then begin
-      TNetData.NetData.MinFutureBlocksToDownloadNewSafebox:=FAppParams.ParamByName[CT_PARAM_MinFutureBlocksToDownloadNewSafebox].GetAsInteger(TNetData.NetData.MinFutureBlocksToDownloadNewSafebox);
+    if TSettings.AppParams.ParamByName[CT_PARAM_AllowDownloadNewCheckpointIfOlderThan].GetAsBoolean(TNetData.NetData.MinFutureBlocksToDownloadNewSafebox>200) then begin
+      TNetData.NetData.MinFutureBlocksToDownloadNewSafebox:=TSettings.AppParams.ParamByName[CT_PARAM_MinFutureBlocksToDownloadNewSafebox].GetAsInteger(TNetData.NetData.MinFutureBlocksToDownloadNewSafebox);
     end else TNetData.NetData.MinFutureBlocksToDownloadNewSafebox:=0;
   end;
 end;

+ 1 - 1
src/gui-classic/UFRMWalletKeys.pas

@@ -556,7 +556,7 @@ begin
     if wk.Name='' then lblKeyName.Caption := '(No name)'
     else lblKeyName.Caption := wk.Name;
     memoPrivateKey.Font.Color := clBlack;
-    s := TAccountComp.AccountPublicKeyExport(wk.AccountKey);
+    s := '@['+TAccountComp.AccountPublicKeyExport(wk.AccountKey)+']'; // PayToPubkey EPASA format
     memoPrivateKey.Lines.Text:=s;
   finally
     lblEncryptionTypeCaption.Enabled := ok;

+ 77 - 40
src/gui-classic/UGridUtils.pas

@@ -31,7 +31,7 @@ uses
   LCLIntf, LCLType, LMessages,
 {$ENDIF}
   Classes, Grids, UNode, UAccounts, UBlockChain, UAppParams, UThread, UPCDataTypes,
-  UWallet, UCrypto, UPoolMining, URPC, UBaseTypes, UPCOrderedLists,
+  UWallet, UCrypto, UPoolMining, URPC, UBaseTypes, UPCOrderedLists, USettings,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 Type
@@ -134,7 +134,7 @@ Type
 
   TOperationsGridUpdateThread = Class(TPCThread)
     FOperationsGrid : TOperationsGrid;
-    procedure DoUpdateOperationsGrid(ANode : TNode; var AList : TList<TOperationResume>);
+    procedure DoUpdateOperationsGrid(const ANode : TNode; const AWalleTKeys : TWalletKeys; const APasswords : TList<String>; var AList : TList<TOperationResume>);
   protected
     procedure BCExecute; override;
   public
@@ -152,6 +152,8 @@ Type
     FBlockEnd: Int64;
     FMustShowAlwaysAnAccount: Boolean;
     FOperationsGridUpdateThread : TOperationsGridUpdateThread;
+    FWalletKeys: TWalletKeys;
+    FPasswords: TList<String>;
     Procedure OnNodeNewOperation(Sender : TObject);
     Procedure OnNodeNewAccount(Sender : TObject);
     Procedure InitGrid;
@@ -177,6 +179,8 @@ Type
     Property AccountNumber : Int64 read FAccountNumber write SetAccountNumber;
     Property MustShowAlwaysAnAccount : Boolean read FMustShowAlwaysAnAccount write SetMustShowAlwaysAnAccount;
     Property Node : TNode read GetNode write SetNode;
+    property WalletKeys : TWalletKeys read FWalletKeys write FWalletKeys;
+    property Passwords : TList<String> read FPasswords;
     Procedure UpdateAccountOperations; virtual;
     Procedure ShowModalDecoder(WalletKeys: TWalletKeys; AppParams : TAppParams);
     Property BlockStart : Int64 read FBlockStart write SetBlockStart;
@@ -230,8 +234,6 @@ Type
 
   { TBlockChainGrid }
 
-  TShowHashRateAs = (hr_Unit, hr_Kilo, hr_Mega, hr_Giga, hr_Tera, hr_Peta, hr_Exa);
-
   TBlockChainGrid = Class(TComponent)
   private
     FBlockChainDataList : TList<TBlockChainData>;
@@ -283,72 +285,76 @@ implementation
 
 uses
   Graphics, SysUtils, UTime, UOpTransaction, UConst,
+  UEPasa, UEPasaDecoder,
   UFRMPayloadDecoder, ULog;
 
 { TAccountsGridUpdateThread }
 
 procedure TAccountsGridUpdateThread.BCExecute;
 Var
-  l : TAccountsNumbersList;
-  i,j, j_min, j_max : Integer;
+  LAccountsNumbersList : TAccountsNumbersList;
+  i,j, j_min : Integer;
   c  : Cardinal;
   LApplyfilter : Boolean;
   LAccount : TAccount;
   LNode : TNode;
+  LAccountsList : TList<Integer>;
 begin
   LApplyfilter := ((FAccountsGridFilter.MinBalance>0) Or ((FAccountsGridFilter.MaxBalance>=0) And (FAccountsGridFilter.MaxBalance<CT_MaxWalletAmount)));
   FBalance := 0;
   LNode := FAccountsGrid.Node;
   try
-      if (Assigned(FAccountsGridFilter.OrderedAccountsKeyList)) then begin
-        if (FAccountsGridFilter.indexAccountsKeyList<0) then i := 0
-        else i := FAccountsGridFilter.indexAccountsKeyList;
+    if (Assigned(FAccountsGridFilter.OrderedAccountsKeyList)) then begin
+      if (FAccountsGridFilter.indexAccountsKeyList<0) then i := 0
+      else i := FAccountsGridFilter.indexAccountsKeyList;
 
-        while (Not Terminated) and (i<FAccountsGridFilter.OrderedAccountsKeyList.Count)
-          and ((FAccountsGridFilter.indexAccountsKeyList<0) or (FAccountsGridFilter.indexAccountsKeyList=i)) do begin
+      while (Not Terminated) and (i<FAccountsGridFilter.OrderedAccountsKeyList.Count)
+        and ((FAccountsGridFilter.indexAccountsKeyList<0) or (FAccountsGridFilter.indexAccountsKeyList=i)) do begin
 
-          j_min := 0;
+        j_min := 0;
 
           while (j_min>=0) do begin
 
           LNode.bank.SafeBox.StartThreadSafe;
           FAccountsGridFilter.OrderedAccountsKeyList.Lock; // Protection v4
           Try
-            l := FAccountsGridFilter.OrderedAccountsKeyList.AccountKeyList[i];
-            if Assigned(l) then begin
-
-              j_max := (j_min + 500);
-              if j_max>=l.Count then j_max := l.Count-1;
-
-              for j := j_min to j_max do begin
-                LAccount := LNode.Bank.SafeBox.Account(l.Get(j));
-                if LApplyfilter then begin
-                  if (LAccount.balance>=FAccountsGridFilter.MinBalance) And ((FAccountsGridFilter.MaxBalance<0) Or (LAccount.balance<=FAccountsGridFilter.MaxBalance)) then begin
+            LAccountsNumbersList := FAccountsGridFilter.OrderedAccountsKeyList.AccountKeyList[i];
+            if Assigned(LAccountsNumbersList) then begin
+
+              LAccountsList := TList<Integer>.Create;
+              Try
+                LAccountsNumbersList.FillList(j_min,500,LAccountsList);
+                for j := 0 to LAccountsList.Count - 1 do begin
+                  LAccount := LNode.Bank.SafeBox.Account(LAccountsList[j]);
+
+                  if LApplyfilter then begin
+                    if (LAccount.balance>=FAccountsGridFilter.MinBalance) And ((FAccountsGridFilter.MaxBalance<0) Or (LAccount.balance<=FAccountsGridFilter.MaxBalance)) then begin
+                      FProcessedList.Add(LAccount.account);
+                      FBalance := FBalance + LAccount.balance;
+                    end;
+                  end else begin
                     FProcessedList.Add(LAccount.account);
                     FBalance := FBalance + LAccount.balance;
                   end;
-                end else begin
-                  FProcessedList.Add(LAccount.account);
-                  FBalance := FBalance + LAccount.balance;
+                  if Terminated then Exit;
                 end;
-                if Terminated then Exit;
-              end;
-              j_min := j_max+1;
-              if (j_max>=(l.Count-1)) then begin
-                j_min := -1;
-                break;
-              end;
+                if LAccountsList.Count>0 then inc(j_min,LAccountsList.Count)
+                else break;
+
+              Finally
+                LAccountsList.Free;
+              End;
+
             end;
           finally
             FAccountsGridFilter.OrderedAccountsKeyList.Unlock;
             LNode.Bank.SafeBox.EndThreadSave;
           end;
-            if j_max>=0 then Sleep(0);
 
-          end;
-          inc(i);
         end;
-      end else begin
+        inc(i);
+      end;
+    end else begin
         c := 0;
         while (c<LNode.Bank.SafeBox.AccountsCount) and (Not Terminated) do begin
           LAccount := LNode.Bank.SafeBox.Account(c);
@@ -895,6 +901,7 @@ procedure TAccountsGrid.UnlockAccountsList;
 begin
   UpdateAccountsBalance;
   InitGridRowCount;
+  if Assigned(FOnUpdated) then FOnUpdated(Self);
 end;
 
 procedure TAccountsGrid.UpdateAccountsBalance;
@@ -939,7 +946,7 @@ var list : TList<TOperationResume>;
 begin
   list := TList<TOperationResume>.Create;
   try
-    DoUpdateOperationsGrid(FOperationsGrid.Node,list);
+    DoUpdateOperationsGrid(FOperationsGrid.Node,FOperationsGrid.WalletKeys,FOperationsGrid.Passwords,list);
     if (Not Terminated) then begin
       FOperationsGrid.FOperationsResume.Clear;
       for i := 0 to list.Count-1 do begin
@@ -960,7 +967,8 @@ begin
   Suspended := False;
 end;
 
-procedure TOperationsGridUpdateThread.DoUpdateOperationsGrid(ANode: TNode; var AList: TList<TOperationResume>);
+procedure TOperationsGridUpdateThread.DoUpdateOperationsGrid(const ANode : TNode; const AWalleTKeys : TWalletKeys;
+  const APasswords : TList<String>; var AList: TList<TOperationResume>);
 Var list : TList<Cardinal>;
   i,j : Integer;
   OPR : TOperationResume;
@@ -969,6 +977,7 @@ Var list : TList<Cardinal>;
   bstart,bend : int64;
   LOperationsResume : TOperationsResumeList;
   LLockedMempool : TPCOperationsComp;
+  LEPasa : TEPasa;
 begin
   if Not Assigned(ANode) then exit;
   AList.Clear;
@@ -1060,6 +1069,13 @@ begin
       end;
     end;
   Finally
+    for i := 0 to AList.Count-1 do begin
+      OPR := AList[i];
+      if TEPasaDecoder.TryDecodeEPASA(OPR.DestAccount,OPR.OriginalPayload,ANode,AWalleTKeys,APasswords,LEPasa) then begin
+        OPR.DecodedEPasaPayload := LEPasa.ToString(True);
+        AList[i] := OPR;
+      end;
+    end;
   End;
 end;
 
@@ -1067,6 +1083,8 @@ end;
 
 constructor TOperationsGrid.Create(AOwner: TComponent);
 begin
+  FPasswords := TList<String>.Create;
+  FWalletKeys := Nil;
   FAccountNumber := 0;
   FDrawGrid := Nil;
   MustShowAlwaysAnAccount := false;
@@ -1090,6 +1108,7 @@ begin
   end;
   FOperationsResume.Free;
   FNodeNotifyEvents.Free;
+  FPasswords.Free;
   inherited;
 end;
 
@@ -1134,8 +1153,9 @@ begin
 end;
 
 procedure TOperationsGrid.OnGridDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
-Var s : String;
+Var s, saux : String;
   opr : TOperationResume;
+  LRectLeft, LRectRight : TRect;
 begin
   DrawGrid.Canvas.Font.Color:=clWindowText;
   opr := CT_TOperationResume_NUL;
@@ -1208,7 +1228,24 @@ begin
         Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
       end else if ACol=7 then begin
         s := opr.PrintablePayload;
-        Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfLeft,tfVerticalCenter,tfSingleLine]);
+        LRectRight := Rect;
+        if opr.OriginalPayload.payload_type>0 then begin
+          saux := '0x'+IntToHex(opr.OriginalPayload.payload_type,2);
+          LRectLeft := Rect;
+          LRectLeft.Width := 30;
+          Rect.Inflate(-32,0,0,0);
+          DrawGrid.Canvas.Font.Color := clBlue;
+          DrawGrid.Canvas.Font.Style := [fsBold];
+          Canvas_TextRect(DrawGrid.Canvas,LRectLeft,saux,State,[tfLeft,tfVerticalCenter,tfSingleLine]);
+          if opr.DecodedEPasaPayload<>'' then begin
+            DrawGrid.Canvas.Font.Style := [fsBold];
+            s := opr.DecodedEPasaPayload
+          end else DrawGrid.Canvas.Font.Style := [];
+        end else if opr.OriginalPayload.payload_raw.ToString=s then begin
+          DrawGrid.Canvas.Font.Style := [fsBold];
+        end;
+        DrawGrid.Canvas.Font.Color := clBlack;
+        Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfLeft,tfVerticalCenter,tfSingleLine])
       end else begin
         s := '(???)';
         Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfCenter,tfVerticalCenter,tfSingleLine]);

+ 2 - 1
src/libraries/abstractmem/ConfigAbstractMem.inc

@@ -49,7 +49,8 @@
   - Fixed bug on CacheMem when replacing initial position of buffer
 
   Version 1.2 - Jan 2021
-  - Added TAbstractBTree - Standard B-Tree implementation for use on AbstractMem Library
+  - Added TAbstractBTree - Standard B-Tree implementation for use on AbstractMem Library - Multithread protected
+  - Added TAbstractMemBTreeData<TData> that implements a generic <TData> implementation for TAbstractBTree on TAbstractMem
   - Added ABSTRACTMEM_CIRCULAR_SEARCH_PROTECTION compiler directive to prevent circular structures on Tree nodes
 
 }

+ 429 - 143
src/libraries/abstractmem/UAbstractBTree.pas

@@ -34,6 +34,7 @@ interface
 
 uses
   Classes, SysUtils,
+  SyncObjs,
   // NOTE ABOUT FREEPASCAL (2020-03-10)
   // Current version 3.0.4 does not contain valid support for Generics, using Generics from this:
   // https://github.com/PascalCoinDev/PascalCoin/tree/master/src/libraries/generics.collections
@@ -74,16 +75,21 @@ type
     FAllowDuplicates: Boolean;
     FOrder: Integer;
     FCircularProtection : Boolean;
-    procedure SplitAfterInsert(var ANode : TAbstractBTreeNode);
+    procedure SplitAfterInsert(var ANode : TAbstractBTreeNode; const ACircularProtectionList : TOrderedList<TIdentify>);
     procedure MoveRange(var ASourceNode, ADestNode : TAbstractBTreeNode; AFromSource, ACount, AToDest : Integer);
     procedure MoveRangeBetweenSiblings(var ASourceNode, ADestNode : TAbstractBTreeNode);
     procedure BTreeNodeToString(const ANode : TAbstractBTreeNode; ALevel, ALevelIndex : Integer; const AStrings : TStrings);
-    procedure CheckConsistencyEx(const ANode: TAbstractBTreeNode; AIsGoingDown : Boolean; AParentDataIndexLeft,AParentDataIndexRight : Integer; ADatas: TOrderedList<TData>; AIdents: TOrderedList<TIdentify>; ACurrentLevel : Integer; var ALevels, ANodesCount, AItemsCount : Integer);
-    function FindPrecessorExt(var ANode : TAbstractBTreeNode; var iPos : Integer) : Boolean;
-    function FindSuccessorExt(var ANode : TAbstractBTreeNode; var iPos : Integer) : Boolean;
+    procedure CheckConsistencyEx(const ANode: TAbstractBTreeNode; AIsGoingDown : Boolean; AParentDataIndexLeft,AParentDataIndexRight : Integer; ADatas: TList<TData>; AIdents: TOrderedList<TIdentify>; ACurrentLevel : Integer; var ALevels, ANodesCount, AItemsCount : Integer);
+    function FindPrecessorExt(const ACircularProtectionList : TOrderedList<TIdentify>; var ANode : TAbstractBTreeNode; var iPos : Integer) : Boolean;
+    function FindSuccessorExt(const ACircularProtectionList : TOrderedList<TIdentify>; var ANode : TAbstractBTreeNode; var iPos : Integer) : Boolean;
     procedure EraseTreeExt(var ANode : TAbstractBTreeNode);
+    function FindExt(const AData: TData; const ACircularProtectionList : TOrderedList<TIdentify>; out ANode : TAbstractBTreeNode; out iPos : Integer): Boolean;
+    function FindLowestNodeExt(const ACircularProtectionList : TOrderedList<TIdentify>): TAbstractBTreeNode;
+    function FindHighestNodeExt(const ACircularProtectionList : TOrderedList<TIdentify>): TAbstractBTreeNode;
   protected
     FCount: integer;
+    FAbstractBTreeLock : TCriticalSection;
+    FIsFindingProcess : Boolean;
     function GetRoot: TAbstractBTreeNode; virtual; abstract;
     procedure SetRoot(var Value: TAbstractBTreeNode); virtual; abstract;
 
@@ -91,15 +97,17 @@ type
     function NewNode : TAbstractBTreeNode; virtual; abstract;
     procedure DisposeNode(var ANode : TAbstractBTreeNode); virtual; abstract;
     procedure SetNil(var AIdentify : TIdentify); virtual; abstract;
-    function BinarySearch(const AData : TData; const ADataArray : TDataArray; out AIndex : Integer) : Boolean;
+    function BinarySearch(const AData : TData; const ADataArray : TDataArray; out AIndex : Integer) : Boolean; virtual;
     function AreEquals(const AIdentify1, AIdentify2 : TIdentify) : Boolean;
     procedure SaveNode(var ANode : TAbstractBTreeNode); virtual; abstract;
     function GetCount : Integer; virtual;
     procedure SetCount(const ANewCount : Integer); virtual;
     function GetHeight: Integer; virtual;
     property Count : Integer read GetCount;
-    procedure CheckConsistencyFinalized(ADatas : TOrderedList<TData>; AIdents : TOrderedList<TIdentify>; Alevels, ANodesCount, AItemsCount : Integer); virtual;
+    procedure CheckConsistencyFinalized(ADatas : TList<TData>; AIdents : TOrderedList<TIdentify>; Alevels, ANodesCount, AItemsCount : Integer); virtual;
     function FindChildPos(const AIdent : TIdentify; const AParent : TAbstractBTreeNode) : Integer;
+    procedure DisposeData(var AData : TData); virtual;
+    function DoCompareData(const ALeftData, ARightData: TData): Integer; virtual;
   public
     property AllowDuplicates : Boolean read FAllowDuplicates write FAllowDuplicates;
     function IsNil(const AIdentify : TIdentify) : Boolean; virtual; abstract;
@@ -115,10 +123,13 @@ type
     function FindLowest(out ALowest : TData) : Boolean;
     function FindHighestNode: TAbstractBTreeNode;
     function FindHighest(out AHighest : TData) : Boolean;
+    function FindIndex(AIndex : Integer; out AData : TData) : Boolean;
+    function FillList(AStartIndex, ACount : Integer; const AList : TList<TData>) : Integer;
     function Add(const AData: TData) : Boolean;
     function Delete(const AData: TData) : Boolean;
     function NodeDataToString(const AData : TData) : String; virtual;
     constructor Create(const AOnCompareIdentifyMethod: TComparison<TIdentify>; const AOnCompareDataMethod: TComparison<TData>; AAllowDuplicates : Boolean; AOrder: Integer);
+    destructor Destroy; override;
     property OnCompareIdentifyMethod: TComparison<TIdentify> read FOnCompareIdentify;
     property OnCompareDataMethod: TComparison<TData> read FOnCompareData;
     function BTreeToString : String;
@@ -130,6 +141,8 @@ type
     procedure CheckConsistency; virtual;
     property Height : Integer read GetHeight;
     property CircularProtection : Boolean read FCircularProtection write FCircularProtection;
+    procedure Lock;
+    procedure Unlock;
   End;
 
   TMemoryBTree<TData> = Class( TAbstractBTree<Integer,TData> )
@@ -145,7 +158,7 @@ type
     procedure DisposeNode(var ANode : TAbstractBTree<Integer,TData>.TAbstractBTreeNode); override;
     procedure SetNil(var AIdentify : Integer); override;
     procedure SaveNode(var ANode : TAbstractBTree<Integer,TData>.TAbstractBTreeNode); override;
-    procedure CheckConsistencyFinalized(ADatas : TOrderedList<TData>; AIdents : TOrderedList<Integer>; Alevels, ANodesCount, AItemsCount : Integer); override;
+    procedure CheckConsistencyFinalized(ADatas : TList<TData>; AIdents : TOrderedList<Integer>; Alevels, ANodesCount, AItemsCount : Integer); override;
   public
     function IsNil(const AIdentify : Integer) : Boolean; override;
     constructor Create(const AOnCompareDataMethod: TComparison<TData>; AAllowDuplicates : Boolean; AOrder : Integer);
@@ -163,14 +176,6 @@ type
     destructor Destroy; override;
   End;
 
-  TIntegerBTree = Class( TMemoryBTree<Integer> )
-  private
-  protected
-  public
-    constructor Create(AAllowDuplicates : Boolean; AOrder : Integer);
-    function NodeDataToString(const AData : Integer) : String; override;
-  End;
-
 implementation
 
 { TAbstractBTree<TIdentify, TData> }
@@ -178,32 +183,47 @@ implementation
 function TAbstractBTree<TIdentify, TData>.Add(const AData: TData): Boolean;
 var Lnode  : TAbstractBTreeNode;
   iDataPos : Integer;
+  LCircularProtectionList : TOrderedList<TIdentify>;
 begin
-  if (Find(AData,Lnode,iDataPos)) then begin
-    if (Not FAllowDuplicates) then Exit(False);
-    // Follow childs until leaf node
-    while (Not Lnode.IsLeaf) do begin
-      Lnode := GetNode(Lnode.childs[iDataPos]); // Insert at right position
-      if (BinarySearch(AData,Lnode.data,iDataPos)) then begin
-        //
+  FAbstractBTreeLock.Acquire;
+  Try
+    if FCircularProtection then begin
+      LCircularProtectionList := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+    end else LCircularProtectionList := Nil;
+    Try
+      if (FindExt(AData,LCircularProtectionList,Lnode,iDataPos)) then begin
+        if (Not FAllowDuplicates) then Exit(False);
+        // Follow childs until leaf node
+        while (Not Lnode.IsLeaf) do begin
+          Lnode := GetNode(Lnode.childs[iDataPos]); // Insert at right position
+          if (BinarySearch(AData,Lnode.data,iDataPos)) then begin
+            //
+          end;
+        end;
+      end else if (IsNil(Lnode.identify)) then begin
+        Lnode := NewNode;
+        SetRoot(Lnode);
       end;
-    end;
-  end else if (IsNil(Lnode.identify)) then begin
-    Lnode := NewNode;
-    SetRoot(Lnode);
-  end;
-  Assert(Lnode.IsLeaf,'Node must be a leaf');
-  // Lnode is a leaf and iDataPos is position to insert
-  Lnode.InsertData(Adata,iDataPos);
-  SaveNode(Lnode);
-  if Lnode.Count>MaxItemsPerNode then begin
-    // Split and up
-    SplitAfterInsert(Lnode);
-  end;
-  Result := True;
-  if (FCount>=0) then begin
-    SetCount(FCount+1);
-  end;
+      Assert(Lnode.IsLeaf,'Node must be a leaf');
+      // Lnode is a leaf and iDataPos is position to insert
+      Lnode.InsertData(Adata,iDataPos);
+      SaveNode(Lnode);
+      if Lnode.Count>MaxItemsPerNode then begin
+        // Split and up
+        if Assigned(LCircularProtectionList) then LCircularProtectionList.Clear;
+        SplitAfterInsert(Lnode,LCircularProtectionList);
+      end;
+      Result := True;
+      if (FCount>=0) then begin
+        SetCount(FCount+1);
+      end;
+    Finally
+      if Assigned(LCircularProtectionList) then
+        LCircularProtectionList.Free;
+    End;
+  Finally
+    FAbstractBTreeLock.Release;
+  End;
 end;
 
 function TAbstractBTree<TIdentify, TData>.AreEquals(const AIdentify1, AIdentify2: TIdentify): Boolean;
@@ -220,7 +240,7 @@ begin
   j := Length(ADataArray)-1;
   while (i <= j) do begin
     mid := (i + j) shr 1;
-    cmp := FOnCompareData(AData,ADataArray[mid]);
+    cmp := DoCompareData(AData,ADataArray[mid]);
     if (cmp<0) then begin
       j := mid - 1;
     end else if (cmp>0) then begin
@@ -255,24 +275,27 @@ var Lsl : TStrings;
   Lnode : TAbstractBTreeNode;
 begin
   Lsl := TStringList.Create;
+  FAbstractBTreeLock.Acquire;
   try
     Lnode := GetRoot;
     if Not IsNil(Lnode.identify) then BTreeNodeToString(Lnode,0,0,Lsl);
     Result := Lsl.Text;
   finally
+    FAbstractBTreeLock.Release;
     Lsl.Free;
   end;
 end;
 
 procedure TAbstractBTree<TIdentify, TData>.CheckConsistency;
 var
-  FDatas : TOrderedList<TData>;
+  FDatas : TList<TData>;
   FIdents : TOrderedList<TIdentify>;
   Lnode : TAbstractBTreeNode;
   Llevels, LnodesCount, LItemsCount : Integer;
 begin
   FIdents := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
-  FDatas := TOrderedList<TData>.Create(FAllowDuplicates,FOnCompareData);
+  FDatas := TList<TData>.Create;
+  FAbstractBTreeLock.Acquire;
   try
     Llevels := 0;
     LnodesCount := 0;
@@ -286,12 +309,13 @@ begin
     end;
     CheckConsistencyFinalized(FDatas,FIdents,Llevels,LnodesCount,LItemsCount);
   finally
+    FAbstractBTreeLock.Release;
     FDatas.Free;
     FIdents.Free;
   end;
 end;
 
-procedure TAbstractBTree<TIdentify, TData>.CheckConsistencyEx(const ANode: TAbstractBTreeNode; AIsGoingDown : Boolean; AParentDataIndexLeft, AParentDataIndexRight : Integer; ADatas: TOrderedList<TData>; AIdents: TOrderedList<TIdentify>; ACurrentLevel : Integer; var ALevels, ANodesCount, AItemsCount : Integer);
+procedure TAbstractBTree<TIdentify, TData>.CheckConsistencyEx(const ANode: TAbstractBTreeNode; AIsGoingDown : Boolean; AParentDataIndexLeft, AParentDataIndexRight : Integer; ADatas: TList<TData>; AIdents: TOrderedList<TIdentify>; ACurrentLevel : Integer; var ALevels, ANodesCount, AItemsCount : Integer);
 var Lchild : TAbstractBTreeNode;
   i, Lcmp, iLeft, iRight : Integer;
 begin
@@ -308,15 +332,15 @@ begin
     if (ANode.Count=0) then raise EAbstractBTree.Create(Format('Inconsistent NIL node at level %d',[ACurrentLevel]));
     if (AParentDataIndexLeft>=0) then begin
       // Right must be < than parent
-      Lcmp := FOnCompareData(ADatas.Get(AParentDataIndexLeft), ANode.data[0]);
+      Lcmp := DoCompareData(ADatas.Items[AParentDataIndexLeft], ANode.data[0]);
       if Lcmp>0 then raise EAbstractBTree.Create(Format('Inconsistent %d data [%s] vs parent left [%s] at level %d',
-        [Lcmp,NodeDataToString(ANode.data[0]),NodeDataToString(ADatas.Get(AParentDataIndexLeft)), ACurrentLevel]));
+        [Lcmp,NodeDataToString(ANode.data[0]),NodeDataToString(ADatas.Items[AParentDataIndexLeft]), ACurrentLevel]));
     end;
     if (AParentDataIndexRight>=0) then begin
       // Right must be < than parent
-      Lcmp := FOnCompareData(ANode.data[ANode.Count-1],ADatas.Get(AParentDataIndexRight));
+      Lcmp := DoCompareData(ANode.data[ANode.Count-1],ADatas.Items[AParentDataIndexRight]);
       if Lcmp>0 then raise EAbstractBTree.Create(Format('Inconsistent %d data [%s] vs parent right [%s] at level %d',
-        [Lcmp,NodeDataToString(ANode.data[ANode.Count-1]),NodeDataToString(ADatas.Get(AParentDataIndexRight)), ACurrentLevel]));
+        [Lcmp,NodeDataToString(ANode.data[ANode.Count-1]),NodeDataToString(ADatas.Items[AParentDataIndexRight]), ACurrentLevel]));
     end;
   end;
   if (MinItemsPerNode>ANode.Count) or (MaxItemsPerNode<ANode.Count) then begin
@@ -326,7 +350,7 @@ begin
   end;
 
   for i := 1 to ANode.Count-1 do begin
-    if FOnCompareData(ANode.data[i-1],ANode.data[i])>0 then raise EAbstractBTree.Create(Format('Inconsistent data (%d..%d)/%d [%s] > [%s] at level %d',
+    if DoCompareData(ANode.data[i-1],ANode.data[i])>0 then raise EAbstractBTree.Create(Format('Inconsistent data (%d..%d)/%d [%s] > [%s] at level %d',
       [i-1,i,ANode.Count,NodeDataToString(ANode.data[i-1]),NodeDataToString(ANode.data[i]), ACurrentLevel]));
   end;
 
@@ -360,7 +384,7 @@ begin
 
 end;
 
-procedure TAbstractBTree<TIdentify, TData>.CheckConsistencyFinalized(ADatas: TOrderedList<TData>; AIdents: TOrderedList<TIdentify>; Alevels, ANodesCount, AItemsCount: Integer);
+procedure TAbstractBTree<TIdentify, TData>.CheckConsistencyFinalized(ADatas: TList<TData>; AIdents: TOrderedList<TIdentify>; Alevels, ANodesCount, AItemsCount: Integer);
 begin
   //
 end;
@@ -375,12 +399,14 @@ end;
 
 constructor TAbstractBTree<TIdentify, TData>.Create(const AOnCompareIdentifyMethod: TComparison<TIdentify>; const AOnCompareDataMethod: TComparison<TData>; AAllowDuplicates : Boolean; AOrder: Integer);
 begin
+  FIsFindingProcess := False;
+  FAbstractBTreeLock := TCriticalSection.Create;
   FOnCompareIdentify := AOnCompareIdentifyMethod;
   FOnCompareData := AOnCompareDataMethod;
   FAllowDuplicates := AAllowDuplicates;
   FOrder := AOrder;
   if FOrder<3 then FOrder := 3 // Minimum order for a BTree is 3. Order = Max childs
-  else if FOrder>32 then FOrder := 32; // Maximum order will be established to 32
+  else if FOrder>255 then FOrder := 255; // Maximum order will be established to 255
   FCount := -1;                 // -1 Means there is no control
   {$IFDEF ABSTRACTMEM_CIRCULAR_SEARCH_PROTECTION}
   FCircularProtection := True;
@@ -394,22 +420,35 @@ var Lnode, Lparent, Lparentparent : TAbstractBTreeNode;
   iPos, iPosParent, iPosParentParent, j : Integer;
   LmovingUp : Boolean;
   Lleft, Lright : TAbstractBTreeNode;
+  LCircularProtectionList: TOrderedList<TIdentify>;
 begin
-  if Not Find(AData,Lnode,iPos) then Exit(False);
+  FAbstractBTreeLock.Acquire;
+  try
+    if FCircularProtection then begin
+      LCircularProtectionList := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+    end else LCircularProtectionList := Nil;
+    try
 
-  Assert(FCount<>0,'Cannot Delete when FCount = 0');
+    if Not FindExt(AData,LCircularProtectionList,Lnode,iPos) then Exit(False);
 
-  if (FCount>0) then begin
-    SetCount(FCount-1);
-  end;
+    Assert(FCount<>0,'Cannot Delete when FCount = 0');
 
-  LmovingUp := False;
+    if (FCount>0) then begin
+      SetCount(FCount-1);
+    end;
 
-  if (Lnode.IsLeaf) then begin
-    Lnode.DeleteData(iPos);
-  end;
+    LmovingUp := False;
+
+    if (Lnode.IsLeaf) then begin
+      Lnode.DeleteData(iPos);
+    end;
+
+    if Assigned(LCircularProtectionList) then LCircularProtectionList.Clear;
 
   repeat
+    if Assigned(LCircularProtectionList) then begin
+      if LCircularProtectionList.Add(Lnode.identify)<0 then raise EAbstractBTree.Create(ClassName+'.Delete Circular T structure at Find for T='+ToString(LNode)+ ' deleting '+NodeDataToString(AData));
+    end;
     if (Lnode.IsLeaf) or (LmovingUp) then begin
       if (IsNil(Lnode.parent)) and (Length(Lnode.childs)=1) then begin
         // child will be root
@@ -432,8 +471,7 @@ begin
       if (Not LmovingUp) then begin
         BinarySearch(AData,Lparent.data,iPosParent);
       end;
-      if (iPosParent>0) //and (iPosParent<=Lparent.Count)
-        then begin
+      if (iPosParent>0) then begin
         Lleft := GetNode(Lparent.childs[iPosParent-1]);
         // Use Left?
         if Lleft.Count>MinItemsPerNode then begin
@@ -577,7 +615,13 @@ begin
       //
       // Search Indorder predecessor:
       Lleft := GetNode(Lnode.childs[iPos]);
-      while (Not Lleft.IsLeaf) do Lleft := GetNode(Lleft.childs[Lleft.Count]);
+      while (Not Lleft.IsLeaf) do begin
+        if Assigned(LCircularProtectionList) then begin
+          if LCircularProtectionList.Add(Lleft.childs[Lleft.Count])<0 then
+            raise EAbstractBTree.Create(ClassName+'.Delete Circular T structure searching for inorder precessor at '+ToString(Lleft)+' deleting '+NodeDataToString(AData));
+        end;
+        Lleft := GetNode(Lleft.childs[Lleft.Count]);
+      end;
       if (Lleft.Count>MinItemsPerNode) then begin
         // Inorder predecessor
         Lnode.data[iPos] := Lleft.data[Lleft.Count-1];
@@ -588,7 +632,13 @@ begin
       end;
       // Search Indorder successor:
       Lright := GetNode(Lnode.childs[iPos+1]);
-      while (Not Lright.IsLeaf) do Lright := GetNode(Lright.childs[0]);
+      while (Not Lright.IsLeaf) do begin
+        if Assigned(LCircularProtectionList) then begin
+          if LCircularProtectionList.Add(Lright.childs[0])<0 then
+            raise EAbstractBTree.Create(ClassName+'.Delete Circular T structure searching for inorder successor at '+ToString(Lright)+' deleting '+NodeDataToString(AData));
+        end;
+        Lright := GetNode(Lright.childs[0]);
+      end;
       if (Lright.Count>MinItemsPerNode) then begin
         // Inorder successor
         Lnode.data[iPos] := Lright.data[0];
@@ -641,18 +691,49 @@ begin
 
     end;
 
-    LmovingUp := True;
+    if (Not LmovingUp) then begin
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Clear;
+      LmovingUp := True;
+    end;
   until (False);
+    finally
+      if Assigned(LCircularProtectionList) then
+        LCircularProtectionList.Free;
+    end;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
+end;
+
+destructor TAbstractBTree<TIdentify, TData>.Destroy;
+begin
+  FAbstractBTreeLock.Free;
+  inherited;
+end;
+
+procedure TAbstractBTree<TIdentify, TData>.DisposeData(var AData: TData);
+begin
+  // Nothing to do
+end;
+
+function TAbstractBTree<TIdentify, TData>.DoCompareData(const ALeftData, ARightData: TData): Integer;
+begin
+  Result := FOnCompareData(ALeftData,ARightData);
 end;
 
 procedure TAbstractBTree<TIdentify, TData>.EraseTree;
 var Lnode : TAbstractBTreeNode;
 begin
-  Lnode := GetRoot;
-  if Not IsNil(Lnode.identify) then EraseTreeExt(Lnode);
-  ClearNode(Lnode);
-  if Fcount>0 then SetCount(0);
-  SetRoot(Lnode);
+  FAbstractBTreeLock.Acquire;
+  try
+    Lnode := GetRoot;
+    if Not IsNil(Lnode.identify) then EraseTreeExt(Lnode);
+    ClearNode(Lnode);
+    if Fcount>0 then SetCount(0);
+    SetRoot(Lnode);
+  finally
+    FAbstractBTreeLock.Release;
+  end;
 end;
 
 procedure TAbstractBTree<TIdentify, TData>.EraseTreeExt(var ANode: TAbstractBTreeNode);
@@ -665,32 +746,69 @@ begin
       EraseTreeExt(Lchild);
     end;
   end;
-  SetLength(ANode.childs,0);
+  for i:=0 to Length(ANode.data)-1 do begin
+    DisposeData(ANode.data[i]);
+  end;
   DisposeNode(ANode);
   ClearNode(ANode);
 end;
 
-function TAbstractBTree<TIdentify, TData>.Find(const AData: TData; out ANode: TAbstractBTreeNode; out iPos: Integer): Boolean;
-var LCircularPreviousSearchProtection : TNoDuplicateData<TIdentify>;
+function TAbstractBTree<TIdentify, TData>.FillList(AStartIndex, ACount: Integer; const AList: TList<TData>): Integer;
+var Lnode : TAbstractBTreeNode;
+  iPos : Integer;
+  LCircularProtectionList: TOrderedList<TIdentify>;
 begin
-  if FCircularProtection then begin
-    LCircularPreviousSearchProtection := TNoDuplicateData<TIdentify>.Create(FOnCompareIdentify);
-  end else LCircularPreviousSearchProtection := Nil;
+  Assert((AStartIndex>=0) and (ACount>=0),Format('Invalid start %d or count %d',[AStartIndex,ACount]));
+  Result := 0;
+  FAbstractBTreeLock.Acquire;
   try
-    ANode := GetRoot;
-    iPos := 0;
-    repeat
-      if FCircularProtection then begin
-        if Not LCircularPreviousSearchProtection.Add(ANode.identify) then raise EAbstractBTree.Create('Circular T structure at Find for T='+ToString(ANode)+ ' searching for '+NodeDataToString(AData));
+    if FCircularProtection then begin
+      LCircularProtectionList := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+    end else LCircularProtectionList := Nil;
+    try
+      if (ACount<=0) or (AStartIndex<0) then Exit;
+      if (FCount>=0) And (FCount-1 < AStartIndex) then Exit;
+
+      Lnode := FindLowestNodeExt(LCircularProtectionList);
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Clear;
+      if Lnode.Count<=0 then Exit;
+      //
+      Dec(AStartIndex);
+      iPos := 0;
+      while (AStartIndex>=0) do begin
+        if Not FindSuccessorExt(LCircularProtectionList,Lnode,iPos) then Exit;
+        Dec(AStartIndex);
       end;
-      if (BinarySearch(AData,ANode.data,iPos)) then Exit(True)
-      else if (Not ANode.IsLeaf) then ANode := GetNode( ANode.childs[ iPos ] )
-      else Exit(False);
-    until False;
+      if Not ( (AStartIndex=-1) and (iPos < Lnode.Count) and (iPos>=0) ) then Exit;
+      // Lnode.data[iPos] = Start position
+      repeat
+        AList.Add(Lnode.data[iPos]);
+        Dec(ACount);
+        inc(Result);
+      until (ACount<0) or (Not FindSuccessorExt(LCircularProtectionList,Lnode,iPos));
+    finally
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Free;
+    end;
   finally
+    FAbstractBTreeLock.Release;
+  end;
+end;
+
+function TAbstractBTree<TIdentify, TData>.Find(const AData: TData; out ANode: TAbstractBTreeNode; out iPos: Integer): Boolean;
+var LCircularProtectionList: TOrderedList<TIdentify>;
+begin
+  FAbstractBTreeLock.Acquire;
+  try
     if FCircularProtection then begin
-      LCircularPreviousSearchProtection.Free;
-    end;
+      LCircularProtectionList := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+    end else LCircularProtectionList := Nil;
+    Try
+      Result := FindExt(AData,LCircularProtectionList,ANode,iPos);
+    Finally
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Free;
+    End;
+  finally
+    FAbstractBTreeLock.Release;
   end;
 end;
 
@@ -702,59 +820,178 @@ begin
   raise EAbstractBTree.Create(Format('Child not found at %s',[ToString(AParent)]));
 end;
 
+function TAbstractBTree<TIdentify, TData>.FindExt(const AData: TData; const ACircularProtectionList: TOrderedList<TIdentify>;
+  out ANode: TAbstractBTreeNode; out iPos: Integer): Boolean;
+begin
+  Assert(Not FIsFindingProcess,'Is finding process');
+  FIsFindingProcess := True;
+  Try
+    ANode := GetRoot;
+    iPos := 0;
+    repeat
+      if Assigned(ACircularProtectionList) then begin
+        if ACircularProtectionList.Add(ANode.identify)<0 then raise EAbstractBTree.Create(ClassName+'.Find Circular T structure at Find for T='+ToString(ANode)+ ' searching for '+NodeDataToString(AData));
+      end;
+      if (BinarySearch(AData,ANode.data,iPos)) then Exit(True)
+      else if (Not ANode.IsLeaf) then ANode := GetNode( ANode.childs[ iPos ] )
+      else Exit(False);
+    until False;
+  Finally
+    FIsFindingProcess := False;
+  End;
+end;
+
 function TAbstractBTree<TIdentify, TData>.FindHighest(out AHighest : TData) : Boolean;
 var Lnode : TAbstractBTreeNode;
 begin
-  Lnode := FindHighestNode;
-  if Lnode.Count>0 then begin
-     AHighest := Lnode.data[Lnode.Count-1];
-     Result := True;
-  end else Result := False;
+  FAbstractBTreeLock.Acquire;
+  try
+    Lnode := FindHighestNode;
+    if Lnode.Count>0 then begin
+       AHighest := Lnode.data[Lnode.Count-1];
+       Result := True;
+    end else Result := False;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
 end;
 
 function TAbstractBTree<TIdentify, TData>.FindHighestNode: TAbstractBTreeNode;
 begin
-  Result := GetRoot;
-  while (Not Result.IsLeaf) do Result := GetNode(Result.childs[Result.Count]);
+  Result := FindHighestNodeExt(Nil);
+end;
+
+function TAbstractBTree<TIdentify, TData>.FindHighestNodeExt(
+  const ACircularProtectionList: TOrderedList<TIdentify>): TAbstractBTreeNode;
+begin
+  FAbstractBTreeLock.Acquire;
+  try
+    Result := GetRoot;
+    while (Not Result.IsLeaf) do begin
+      if Assigned(ACircularProtectionList) then begin
+        if ACircularProtectionList.Add(Result.childs[Result.Count])<0 then
+          raise EAbstractBTree.Create(ClassName+'.FindHighestNode Circular T structure for T='+ToString(Result));
+      end;
+      Result := GetNode(Result.childs[Result.Count]);
+    end;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
+end;
+
+function TAbstractBTree<TIdentify, TData>.FindIndex(AIndex: Integer; out AData: TData): Boolean;
+var Lnode : TAbstractBTreeNode;
+  iPos : Integer;
+  LCircularProtectionList: TOrderedList<TIdentify>;
+begin
+  FAbstractBTreeLock.Acquire;
+  try
+    if FCircularProtection then begin
+      LCircularProtectionList := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+    end else LCircularProtectionList := Nil;
+    try
+      Lnode := FindLowestNodeExt(LCircularProtectionList);
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Clear;
+      if Lnode.Count<=0 then Exit(False);
+      //
+      Dec(AIndex);
+      iPos := 0;
+      while (AIndex>=0) do begin
+        if Not FindSuccessorExt(LCircularProtectionList,Lnode,iPos) then Exit(False);
+        Dec(AIndex);
+      end;
+      if (AIndex=-1) and (iPos < Lnode.Count) and (iPos>=0) then begin
+        Result := True;
+        AData := Lnode.data[iPos];
+      end else Result := False;
+    finally
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Free;
+    end;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
 end;
 
 function TAbstractBTree<TIdentify, TData>.FindLowest(out ALowest : TData) : Boolean;
 var Lnode : TAbstractBTreeNode;
 begin
-  Lnode := FindLowestNode;
-  if Lnode.Count>0 then begin
-    ALowest := Lnode.data[0];
-    Result := True;
-  end else Result := False;
+  FAbstractBTreeLock.Acquire;
+  try
+    Lnode := FindLowestNode;
+    if Lnode.Count>0 then begin
+      ALowest := Lnode.data[0];
+      Result := True;
+    end else Result := False;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
 end;
 
 function TAbstractBTree<TIdentify, TData>.FindLowestNode: TAbstractBTreeNode;
 begin
-  Result := GetRoot;
-  while (Not Result.IsLeaf) do Result := GetNode(Result.childs[0]);
+  Result := FindLowestNodeExt(Nil);
+end;
+
+function TAbstractBTree<TIdentify, TData>.FindLowestNodeExt(
+  const ACircularProtectionList: TOrderedList<TIdentify>): TAbstractBTreeNode;
+begin
+  FAbstractBTreeLock.Acquire;
+  try
+    Result := GetRoot;
+    while (Not Result.IsLeaf) do begin
+      if Assigned(ACircularProtectionList) then begin
+        if ACircularProtectionList.Add(Result.childs[0])<0 then
+          raise EAbstractBTree.Create(ClassName+'.FindLowestNode Circular T structure for T='+ToString(Result));
+      end;
+      Result := GetNode(Result.childs[0]);
+    end;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
 end;
 
 function TAbstractBTree<TIdentify, TData>.FindPrecessor(const AData : TData; out APrecessor : TData) : Boolean;
 var Lnode : TAbstractBTreeNode;
   iPos : Integer;
+  LCircularProtectionList: TOrderedList<TIdentify>;
 begin
-  Result := False;
-  if Not Find(AData,Lnode,iPos) then Exit(False);
-  repeat
-    Result := FindPrecessorExt(Lnode,iPos);
-    if Result then begin
-      APrecessor := Lnode.data[iPos];
-    end;
-  until (Not Result) or (Not FAllowDuplicates) or (FOnCompareData(AData,APrecessor)>0);
+  FAbstractBTreeLock.Acquire;
+  try
+    if FCircularProtection then begin
+      LCircularProtectionList := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+    end else LCircularProtectionList := Nil;
+    Try
+      Result := False;
+      if Not FindExt(AData,LCircularProtectionList,Lnode,iPos) then Exit(False);
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Clear;
+      repeat
+        Result := FindPrecessorExt(LCircularProtectionList,Lnode,iPos);
+        if Result then begin
+          APrecessor := Lnode.data[iPos];
+        end;
+      until (Not Result) or (Not FAllowDuplicates) or (DoCompareData(AData,APrecessor)>0);
+    Finally
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Free;
+    End;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
 end;
 
-function TAbstractBTree<TIdentify, TData>.FindPrecessorExt(var ANode: TAbstractBTreeNode; var iPos: Integer): Boolean;
+function TAbstractBTree<TIdentify, TData>.FindPrecessorExt(const ACircularProtectionList : TOrderedList<TIdentify>; var ANode: TAbstractBTreeNode; var iPos: Integer): Boolean;
 var Lparent : TAbstractBTreeNode;
+  Lsecondary : TOrderedList<TIdentify>;
 begin
   Result := False;
   if (Not ANode.IsLeaf) then begin
     ANode := GetNode(ANode.childs[iPos]);
-    while (Not ANode.IsLeaf) do ANode := GetNode(ANode.childs[ANode.Count]);
+    while (Not ANode.IsLeaf) do begin
+      if Assigned(ACircularProtectionList) then begin
+        if ACircularProtectionList.Add(ANode.childs[ANode.Count])<0 then
+          raise EAbstractBTree.Create(ClassName+'.FindPrecessor Circular T structure at Find for T='+ToString(ANode));
+      end;
+      ANode := GetNode(ANode.childs[ANode.Count]);
+    end;
     iPos := ANode.Count-1;
     Exit(True);
   end else begin
@@ -771,10 +1008,21 @@ begin
         Exit(True);
       end else begin
         // Search parents until parent iPos>0
-        while (iPos=0) and (Not IsNil(Lparent.parent)) do begin
-          ANode := Lparent;
-          Lparent := GetNode(ANode.parent);
-          iPos := FindChildPos(ANode.identify,Lparent);
+        if Assigned(ACircularProtectionList) then begin
+          Lsecondary := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+        end else Lsecondary := Nil;
+        try
+          while (iPos=0) and (Not IsNil(Lparent.parent)) do begin
+            ANode := Lparent;
+            if Assigned(Lsecondary) then begin
+              if Lsecondary.Add(ANode.parent)<0 then
+                raise EAbstractBTree.Create(ClassName+'.FindPrecessor Circular T structure at Find for parent of T='+ToString(ANode));
+            end;
+            Lparent := GetNode(ANode.parent);
+            iPos := FindChildPos(ANode.identify,Lparent);
+          end;
+        finally
+          if Assigned(Lsecondary) then Lsecondary.Free;
         end;
         if iPos>0 then begin
           Dec(iPos);
@@ -789,25 +1037,46 @@ end;
 function TAbstractBTree<TIdentify, TData>.FindSuccessor(const AData : TData; out ASuccessor : TData) : Boolean;
 var Lnode : TAbstractBTreeNode;
   iPos : Integer;
+  LCircularProtectionList: TOrderedList<TIdentify>;
 begin
-  Result := False;
-  if Not Find(AData,Lnode,iPos) then Exit(False);
-  repeat
-    Result := FindSuccessorExt(Lnode,iPos);
-    if Result then begin
-      ASuccessor := Lnode.data[iPos];
-    end;
-  until (Not Result) or (Not FAllowDuplicates) or (FOnCompareData(AData,ASuccessor)<0);
+  FAbstractBTreeLock.Acquire;
+  try
+    if FCircularProtection then begin
+      LCircularProtectionList := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+    end else LCircularProtectionList := Nil;
+    Try
+      Result := False;
+      if Not FindExt(AData,LCircularProtectionList,Lnode,iPos) then Exit(False);
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Clear;
+      repeat
+        Result := FindSuccessorExt(LCircularProtectionList,Lnode,iPos);
+        if Result then begin
+          ASuccessor := Lnode.data[iPos];
+        end;
+      until (Not Result) or (Not FAllowDuplicates) or (DoCompareData(AData,ASuccessor)<0);
+    Finally
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Free;
+    End;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
 end;
 
-function TAbstractBTree<TIdentify, TData>.FindSuccessorExt(var ANode: TAbstractBTreeNode; var iPos: Integer): Boolean;
+function TAbstractBTree<TIdentify, TData>.FindSuccessorExt(const ACircularProtectionList : TOrderedList<TIdentify>; var ANode: TAbstractBTreeNode; var iPos: Integer): Boolean;
 var Lparent : TAbstractBTreeNode;
+  Lsecondary : TOrderedList<TIdentify>;
 begin
   Result := False;
   if (Not ANode.IsLeaf) then begin
     ANode := GetNode(ANode.childs[iPos+1]);
     iPos := 0;
-    while (Not ANode.IsLeaf) do ANode := GetNode(ANode.childs[0]);
+    while (Not ANode.IsLeaf) do begin
+      if Assigned(ACircularProtectionList) then begin
+        if ACircularProtectionList.Add(ANode.childs[0])<0 then
+          raise EAbstractBTree.Create(ClassName+'.FindSuccessor Circular T structure at Find for T='+ToString(ANode));
+      end;
+      ANode := GetNode(ANode.childs[0]);
+    end;
     Exit(True);
   end else begin
     if iPos+1<ANode.Count then begin
@@ -822,10 +1091,21 @@ begin
         Exit(True);
       end else begin
         // Search parents until parent iPos>0
-        while (iPos=Lparent.Count) and (Not IsNil(Lparent.parent)) do begin
-          ANode := Lparent;
-          Lparent := GetNode(ANode.parent);
-          iPos := FindChildPos(ANode.identify,Lparent);
+        if Assigned(ACircularProtectionList) then begin
+          Lsecondary := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
+        end else Lsecondary := Nil;
+        try
+          while (iPos=Lparent.Count) and (Not IsNil(Lparent.parent)) do begin
+            ANode := Lparent;
+            if Assigned(Lsecondary) then begin
+              if Lsecondary.Add(ANode.parent)<0 then
+                raise EAbstractBTree.Create(ClassName+'.FindSuccessor Circular T structure at Find for parent of T='+ToString(ANode));
+            end;
+            Lparent := GetNode(ANode.parent);
+            iPos := FindChildPos(ANode.identify,Lparent);
+          end;
+        finally
+          if Assigned(Lsecondary) then Lsecondary.Free;
         end;
         if iPos<Lparent.Count then begin
           ANode := Lparent;
@@ -844,6 +1124,8 @@ end;
 function TAbstractBTree<TIdentify, TData>.GetHeight: Integer;
 var Lnode : TAbstractBTreeNode;
 begin
+  FAbstractBTreeLock.Acquire;
+  try
   Lnode := GetRoot;
   if (Lnode.Count=0) or (IsNil(Lnode.identify)) then Exit(0);
   Result := 1;
@@ -851,6 +1133,14 @@ begin
     Lnode := GetNode(Lnode.childs[0]);
     inc(Result);
   end;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
+end;
+
+procedure TAbstractBTree<TIdentify, TData>.Lock;
+begin
+  FAbstractBTreeLock.Acquire;
 end;
 
 function TAbstractBTree<TIdentify, TData>.MaxChildrenPerNode: Integer;
@@ -953,7 +1243,7 @@ begin
   FCount := ANewCount;
 end;
 
-procedure TAbstractBTree<TIdentify, TData>.SplitAfterInsert(var ANode: TAbstractBTreeNode);
+procedure TAbstractBTree<TIdentify, TData>.SplitAfterInsert(var ANode: TAbstractBTreeNode; const ACircularProtectionList : TOrderedList<TIdentify>);
 var iDataInsertPos : Integer;
   LnewNode, Lup : TAbstractBTreeNode;
 begin
@@ -965,6 +1255,9 @@ begin
     // Lup will be a new root
     Lup := NewNode;
   end else begin
+    if Assigned(ACircularProtectionList) then begin
+      if ACircularProtectionList.Add(ANode.parent)<0 then raise EAbstractBTree.Create(ClassName+'.SplitAfterInsert Circular T structure at Find for parent of T='+ToString(ANode));
+    end;
     Lup := GetNode(ANode.parent);
   end;
   if Lup.Count=0 then begin
@@ -988,7 +1281,7 @@ begin
   // Remove data&child
   ANode.DeleteData(MinItemsPerNode);
   SaveNode(ANode);
-  if Lup.Count>MaxItemsPerNode then SplitAfterInsert(Lup);
+  if Lup.Count>MaxItemsPerNode then SplitAfterInsert(Lup,ACircularProtectionList);
 end;
 
 function TAbstractBTree<TIdentify, TData>.ToString(const ANode: TAbstractBTreeNode): String;
@@ -1002,6 +1295,11 @@ begin
   Result := '['+Result+']';
 end;
 
+procedure TAbstractBTree<TIdentify, TData>.Unlock;
+begin
+  FAbstractBTreeLock.Release;
+end;
+
 { TAbstractBTree<TIdentify, TData>.TAbstractBTreeNode }
 
 function TAbstractBTree<TIdentify, TData>.TAbstractBTreeNode.Count: Integer;
@@ -1079,7 +1377,7 @@ end;
 
 { TMemoryBTree<TData> }
 
-procedure TMemoryBTree<TData>.CheckConsistencyFinalized(ADatas: TOrderedList<TData>; AIdents: TOrderedList<Integer>; Alevels, ANodesCount, AItemsCount: Integer);
+procedure TMemoryBTree<TData>.CheckConsistencyFinalized(ADatas: TList<TData>; AIdents: TOrderedList<Integer>; Alevels, ANodesCount, AItemsCount: Integer);
 var i,iPos,nDisposed, LDisposedMinPos : Integer;
 begin
   inherited;
@@ -1191,18 +1489,6 @@ begin
   Froot := Value.identify;
 end;
 
-{ TIntegerBTree }
-
-constructor TIntegerBTree.Create(AAllowDuplicates: Boolean; AOrder: Integer);
-begin
-  inherited Create(TComparison_Integer,AAllowDuplicates,AOrder);
-end;
-
-function TIntegerBTree.NodeDataToString(const AData: Integer): String;
-begin
-  Result := AData.ToString;
-end;
-
 { TNoDuplicateData<TData> }
 
 function TNoDuplicateData<TData>.Add(const AData: TData): Boolean;

+ 536 - 0
src/libraries/abstractmem/UAbstractMemBTree.pas

@@ -0,0 +1,536 @@
+unit UAbstractMemBTree;
+
+{
+  This file is part of AbstractMem framework
+
+  Copyright (C) 2020-2021 Albert Molina - [email protected]
+
+  https://github.com/PascalCoinDev/
+
+  *** BEGIN LICENSE BLOCK *****
+
+  The contents of this files are subject to the Mozilla Public License Version
+  2.0 (the "License"); you may not use this file except in compliance with
+  the License. You may obtain a copy of the License at
+  http://www.mozilla.org/MPL
+
+  Software distributed under the License is distributed on an "AS IS" basis,
+  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+  for the specific language governing rights and limitations under the License.
+
+  The Initial Developer of the Original Code is Albert Molina.
+
+  See ConfigAbstractMem.inc file for more info
+
+  ***** END LICENSE BLOCK *****
+}
+
+{$ifdef FPC}
+  {$mode DELPHI}
+{$endif}
+{$H+}
+
+interface
+
+uses
+  Classes, SysUtils,
+  // NOTE ABOUT FREEPASCAL (2020-03-10)
+  // Current version 3.0.4 does not contain valid support for Generics, using Generics from this:
+  // https://github.com/PascalCoinDev/PascalCoin/tree/master/src/libraries/generics.collections
+  // (Download and set folder as a "units include folder" in compiler options)
+  {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults,{$ELSE}Generics.Collections,Generics.Defaults,{$ENDIF}
+  UOrderedList, UAbstractMem, UAbstractBTree;
+
+{$I ./ConfigAbstractMem.inc }
+
+type
+  EAbstractMemBTree = Class(Exception);
+
+  TAbstractMemBTree = Class( TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition> )
+    // BTree implementation on AbstractMem will use TIdentify and TData as a TAbstractMemPosition (aka pointer inside AbstractMem)
+    // Internal search process will convert TData pointer to final TData value for
+    // comparisions
+  private
+    const CT_MIN_INITIAL_POSITION_SIZE = 16;
+          CT_AbstractMemBTree_Magic = 'AMBT'; // DO NOT LOCALIZE MUST BE 4 BYTES LENGTH
+    var
+    FInitialZone : TAMZone;
+    FrootPosition : TAbstractMemPosition;
+    procedure SaveHeader;
+    Procedure CheckInitialized;
+    procedure LoadNodeHeader(const APosition : TAbstractMemPosition; var ANode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode; var AChildsCount : Integer; var AChildsPosition : TAbstractMemPosition);
+    procedure SaveNodeHeader(const ANode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode; const AChildsPosition : TAbstractMemPosition);
+  protected
+    FAbstractMem : TAbstractMem;
+    function GetRoot: TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode; override;
+    procedure SetRoot(var Value: TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode); override;
+    function NewNode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode; override;
+    procedure DisposeNode(var ANode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode); override;
+    procedure SetNil(var AIdentify : TAbstractMemPosition); override;
+    procedure SaveNode(var ANode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode); override;
+    procedure SetCount(const ANewCount : Integer); override;
+    //
+    // NOTE: inherited classes will need to override DisposeData if Data is not a new AbstractMem memory region that must be freed
+    //
+    procedure DisposeData(var AData : TAbstractMemPosition); override;
+    //
+    // NOTE: inherited classes will need to override DoCompareData function in order to properly compare:
+    // function DoCompareData(const ALeftData, ARightData: TAbstractMemPosition): Integer; override;
+    //
+  public
+    function IsNil(const AIdentify : TAbstractMemPosition) : Boolean; override;
+    constructor Create(AAbstractMem : TAbstractMem; const AInitialZone: TAMZone; AAllowDuplicates : Boolean; AOrder : Integer); virtual;
+    destructor Destroy; override;
+    function GetNode(AIdentify : TAbstractMemPosition) : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode; override;
+    class function MinAbstractMemInitialPositionSize : Integer;
+    property AbstractMem : TAbstractMem read FAbstractMem;
+    property Count;
+  End;
+
+  TAbstractMemBTreeData<TData> = Class(TAbstractMemBTree)
+  private
+    // FLeft_ and FRight_ will be used as a cache for improvement calls on DoCompareData
+    FLeft_Pos, FRight_Pos : TAbstractMemPosition;
+    FLeft_Data, FRight_Data : TData;
+    FSearchTarget : TData;
+    FOnCompareAbstractMemData: TComparison<TData>;
+  protected
+    function DoCompareData(const ALeftData, ARightData: TAbstractMemPosition): Integer; override;
+    //
+    function LoadData(const APosition : TAbstractMemPosition) : TData; virtual; abstract;
+    function SaveData(const AData : TData) : TAMZone; virtual; abstract;
+  public
+    constructor Create(AAbstractMem : TAbstractMem; const AInitialZone: TAMZone; AAllowDuplicates : Boolean; AOrder : Integer; const AOnCompareAbstractMemDataMethod: TComparison<TData>);
+    function AddData(const AData: TData) : Boolean;
+    function FindData(const AData: TData; var APosition : TAbstractMemPosition) : Boolean;
+    function DeleteData(const AData: TData) : Boolean;
+    function FindDataPrecessor(const AData : TData; var APrecessor : TData) : Boolean;
+    function FindDataSuccessor(const AData : TData; var ASuccessor : TData) : Boolean;
+    function FindDataLowest(out ALowest : TData) : Boolean;
+    function FindDataHighest(out AHighest : TData) : Boolean;
+  End;
+
+
+
+implementation
+
+{ TAbstractMemBTree<TData> }
+
+procedure TAbstractMemBTree.CheckInitialized;
+begin
+  if (FInitialZone.position=0) then raise EAbstractMemBTree.Create(Format('%s initial position not initialized',[ClassName]));
+end;
+
+constructor TAbstractMemBTree.Create(AAbstractMem : TAbstractMem; const AInitialZone: TAMZone; AAllowDuplicates: Boolean; AOrder: Integer);
+var LBuff : TBytes;
+ i : Integer;
+ LOrder : Integer;
+begin
+  FAbstractMem := AAbstractMem;
+  FrootPosition := 0;
+
+  inherited Create(TComparison_Integer,TComparison_Integer,AAllowDuplicates,AOrder);
+  FCount := 0;
+  //
+  if Not FAbstractMem.GetUsedZoneInfo(AInitialZone.position,False,FInitialZone) then begin
+    if FAbstractMem.ReadOnly then begin
+      // Is not initialized and is Read Only
+      FInitialZone.Clear;
+      Exit;
+    end;
+    raise EAbstractMemBTree.Create('Cannot capture zone info for initialize');
+  end else begin
+    if FInitialZone.position=0 then Exit;
+  end;
+  if (FInitialZone.size<MinAbstractMemInitialPositionSize) then begin
+    raise EAbstractMemBTree.Create(Format('Invalid size %d for initialize',[FInitialZone.size]));
+  end;
+  SetLength(LBuff,CT_MIN_INITIAL_POSITION_SIZE);
+  FAbstractMem.Read(FInitialZone.position,LBuff[0],Length(LBuff));
+  try
+    // Check magic
+    for i := 0 to CT_AbstractMemBTree_Magic.Length-1 do begin
+      if LBuff[i]<>Ord(CT_AbstractMemBTree_Magic.Chars[i]) then Exit;
+    end;
+    Move(LBuff[4],FrootPosition,4);
+    Move(LBuff[8],FCount,4);
+    LOrder := 0;
+    Move(LBuff[12],LOrder,4);
+    if LOrder<>Order then raise EAbstractMemBTree.Create(Format('Invalid Order %d expected %d',[LOrder,Order]));
+    if (((FrootPosition=0) and (FCount>0))) then raise EAbstractMemBTree.Create(Format('Invalid initial root %d vs count %d',[FrootPosition,FCount]));
+  finally
+  end;
+end;
+
+destructor TAbstractMemBTree.Destroy;
+begin
+  //
+  inherited;
+end;
+
+procedure TAbstractMemBTree.DisposeData(var AData: TAbstractMemPosition);
+begin
+  inherited;
+  // Will be called on EraseTreeEx
+  FAbstractMem.Dispose(AData);
+end;
+
+procedure TAbstractMemBTree.DisposeNode(var ANode: TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode);
+var LOld : TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode;
+  LChildsCount : Integer;
+  LChildsPosition : TAbstractMemPosition;
+begin
+  LoadNodeHeader(ANode.identify,LOld,LChildsCount,LChildsPosition);
+  FAbstractMem.Dispose( ANode.identify );
+  ClearNode(ANode);
+  Assert(((LChildsCount=0) and (LChildsPosition=0))
+     or ((LChildsCount<>0) and (LChildsPosition<>0)),Format('Invalid Childs count %d and position %d',[LChildsCount,LChildsPosition]));
+  if LChildsCount>0 then begin
+    FAbstractMem.Dispose( LChildsPosition );
+  end;
+end;
+
+function TAbstractMemBTree.GetNode(AIdentify: TAbstractMemPosition): TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode;
+var LBuff : TBytes;
+  i, LChildsCount : Integer;
+  LChildsPosition : TAbstractMemPosition;
+begin
+  LoadNodeHeader(AIdentify,Result,LChildsCount,LChildsPosition);
+  if LChildsCount>0 then begin
+    SetLength(Result.childs,LChildsCount);
+    SetLength(LBuff,(LChildsCount*4));
+    FAbstractMem.Read(LChildsPosition,LBuff[0],Length(LBuff));
+    for i := 0 to LChildsCount-1 do begin
+      Move(LBuff[i*4],Result.childs[i],4);
+    end;
+  end;
+  if ((Result.Count=0) and (Result.parent=0) and (LChildsCount=0)) then begin
+    // root without data
+  end else begin
+    if (Result.parent=0) then begin
+      if ((Result.Count<1) or (Result.Count>MaxItemsPerNode)) then
+        raise EAbstractMemBTree.Create(Format('Root Node items %d not in range [%d..%d]',[Result.Count,MinItemsPerNode,MaxItemsPerNode]));
+    end else begin
+      if ((Result.Count<MinItemsPerNode) or (Result.Count>MaxItemsPerNode)) then
+        raise EAbstractMemBTree.Create(Format('Node items %d not in range [%d..%d]',[Result.Count,MinItemsPerNode,MaxItemsPerNode]));
+    end;
+    if ((LChildsCount<>0) and (LChildsCount<>(Result.Count+1))) then
+      raise EAbstractMemBTree.Create(Format('Node childrens %d not %d+1 in range [%d..%d]',[LChildsCount,Result.Count,MinChildrenPerNode,MaxChildrenPerNode]));
+  end;
+end;
+
+function TAbstractMemBTree.GetRoot: TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode;
+begin
+  if FrootPosition>0 then begin
+    Result := GetNode(FrootPosition);
+  end else ClearNode(Result);
+end;
+
+function TAbstractMemBTree.IsNil(const AIdentify: TAbstractMemPosition): Boolean;
+begin
+  Result := AIdentify=0;
+end;
+
+procedure TAbstractMemBTree.LoadNodeHeader(
+  const APosition : TAbstractMemPosition; var ANode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode; var AChildsCount : Integer; var AChildsPosition : TAbstractMemPosition);
+var LBuff : TBytes;
+  i, LItemsCount : Integer;
+begin
+  // Node is stored in zone 2 positions:
+  //
+  // Zone 1: Header
+  //   Size = (4+2+2+4) + (4*MaxItemsPerNode)
+  // 4 Bytes [0..3] : Parent
+  // 1 Byte  [4]    : Used items (0..255)
+  // 1 Byte  [5]    : Used childs (0 (leaf) or Used Items+1)
+  // 2 Bytes [6..7] : 0 (unusued)
+  // 4 Bytes [8..11]: Zone 2 position ( If is a leaf must be 0 )
+  // For each item:
+  //   4 Bytes : data (AbstractMemPosition or Data using 4 bytes)
+  //
+  // Zone 2: OPTIONAL Only if NOT a leaf
+  // For each children:
+  //   4 Bytes : Children AbstractMem position
+  //
+  SetLength(LBuff, 8 + (4 * MaxItemsPerNode) + 4 );
+  FAbstractMem.Read(APosition,LBuff[0],Length(LBuff));
+  ClearNode(ANode);
+  LItemsCount := 0;
+  AChildsCount := 0;
+  ANode.identify := APosition;
+  Move(LBuff[0],ANode.parent,4);
+  Move(LBuff[4],LItemsCount,1);
+  Move(LBuff[5],AChildsCount,1);
+  Move(LBuff[8],AChildsPosition,4);
+  SetLength(ANode.data,LItemsCount);
+  for i := 0 to LItemsCount-1 do begin
+    Move(LBuff[12 + (i*4)], ANode.data[i], 4);
+  end;
+end;
+
+class function TAbstractMemBTree.MinAbstractMemInitialPositionSize: Integer;
+begin
+  Result := CT_MIN_INITIAL_POSITION_SIZE;
+end;
+
+function TAbstractMemBTree.NewNode: TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode;
+begin
+  CheckInitialized;
+  ClearNode(Result);
+  Result.identify := FAbstractMem.New( 8 + (4 * MaxItemsPerNode) + 4 ).position;
+  SaveNodeHeader(Result,0);
+end;
+
+procedure TAbstractMemBTree.SaveHeader;
+var LBuff : TBytes;
+ i : Integer;
+ LOrder : Integer;
+begin
+  CheckInitialized;
+  SetLength(LBuff,16);
+  for i := 0 to CT_AbstractMemBTree_Magic.Length-1 do begin
+    LBuff[i] := Byte(Ord(CT_AbstractMemBTree_Magic.Chars[i]));
+  end;
+  Move(FrootPosition,LBuff[4],4);
+  Move(FCount,LBuff[8],4);
+  LOrder := Order;
+  Move(LOrder,LBuff[12],4);
+  FAbstractMem.Write(FInitialZone.position,LBuff[0],16);
+end;
+
+procedure TAbstractMemBTree.SaveNode(var ANode: TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode);
+var LBuff : TBytes;
+  i, LChildsCount : Integer;
+  LOld : TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode;
+  LChildsPosition : TAbstractMemPosition;
+  LZone : TAMZone;
+begin
+  CheckInitialized;
+  if ((ANode.Count)>MaxItemsPerNode) or (Length(ANode.childs)>MaxChildrenPerNode) then begin
+    // Protection against saving temporal Node info with extra datas or childs
+    Exit;
+  end;
+  LoadNodeHeader(ANode.identify,LOld,LChildsCount,LChildsPosition);
+  //
+  if (LChildsCount>0) And (ANode.IsLeaf) then begin
+    // Node wasn't a leaf previously
+    Assert(LChildsPosition<>0,'Old childs position<>0');
+    FAbstractMem.Dispose(LChildsPosition);
+  end else if (LChildsCount=0) And (Not ANode.IsLeaf) then begin
+    // Node was a leaf previously, now not
+    LZone := FAbstractMem.New( MaxChildrenPerNode * 4 );
+    LChildsPosition := LZone.position;
+  end;
+  LChildsCount := Length(ANode.childs);
+  //
+  SaveNodeHeader(ANode,LChildsPosition);
+  //
+  SetLength(LBuff, MaxChildrenPerNode * 4 );
+  FillChar(LBuff[0],Length(LBuff),0);
+  for i := 0 to LChildsCount-1 do begin
+    Move(ANode.childs[i],LBuff[i*4],4);
+  end;
+  FAbstractMem.Write(LChildsPosition,LBuff[0],LChildsCount*4);
+end;
+
+procedure TAbstractMemBTree.SaveNodeHeader(
+  const ANode: TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode; const AChildsPosition : TAbstractMemPosition);
+var LBuff : TBytes;
+  i, LItemsCount, LChildsCount : Integer;
+begin
+  SetLength(LBuff, 8 + (4 * MaxItemsPerNode) + 4 );
+  FillChar(LBuff[0],Length(LBuff),0);
+  Move(ANode.parent,LBuff[0],4);
+  LItemsCount := ANode.Count;
+  Move(LItemsCount,LBuff[4],1);
+  LChildsCount := Length(ANode.childs);
+  Move(LChildsCount,LBuff[5],1);
+  Move(AChildsPosition,LBuff[8],4);
+  for i := 0 to LItemsCount-1 do begin
+    Move(ANode.data[i], LBuff[12 + (i*4)], 4);
+  end;
+  FAbstractMem.Write(ANode.identify,LBuff[0],Length(LBuff));
+end;
+
+procedure TAbstractMemBTree.SetCount(const ANewCount: Integer);
+begin
+  inherited;
+  SaveHeader;
+end;
+
+procedure TAbstractMemBTree.SetNil(var AIdentify: TAbstractMemPosition);
+begin
+  inherited;
+  AIdentify := 0;
+end;
+
+procedure TAbstractMemBTree.SetRoot(var Value: TAbstractBTree<TAbstractMemPosition, TAbstractMemPosition>.TAbstractBTreeNode);
+begin
+  CheckInitialized;
+  inherited;
+  FrootPosition := Value.identify;
+  SaveHeader;
+end;
+
+{ TAbstractMemBTreeData<TData> }
+
+function TAbstractMemBTreeData<TData>.AddData(const AData: TData): Boolean;
+var Lzone : TAMZone;
+begin
+  Lzone := SaveData(AData);
+  Result := inherited Add(Lzone.position);
+  if Not Result then begin
+    // Dispose
+    FAbstractMem.Dispose(Lzone);
+  end;
+end;
+
+constructor TAbstractMemBTreeData<TData>.Create(AAbstractMem: TAbstractMem;
+  const AInitialZone: TAMZone; AAllowDuplicates: Boolean; AOrder: Integer;
+  const AOnCompareAbstractMemDataMethod: TComparison<TData>);
+begin
+  inherited Create(AAbstractMem,AInitialZone,AAllowDuplicates,AOrder);
+  FOnCompareAbstractMemData := AOnCompareAbstractMemDataMethod;
+  FLeft_Pos  := 0;
+  FRight_Pos := 0;
+end;
+
+function TAbstractMemBTreeData<TData>.DeleteData(const AData: TData): Boolean;
+var LAbstractMemPos : TAbstractMemPosition;
+begin
+  if FindData(AData,LAbstractMemPos) then begin
+    Delete(LAbstractMemPos);
+    FAbstractMem.Dispose(LAbstractMemPos);
+    Result := True;
+    if FLeft_Pos=LAbstractMemPos then FLeft_Pos := 0;
+    if FRight_Pos=LAbstractMemPos then FRight_Pos := 0;
+  end else Result := False;
+end;
+
+function TAbstractMemBTreeData<TData>.DoCompareData(const ALeftData, ARightData: TAbstractMemPosition): Integer;
+var Ltmp : TData;
+begin
+  Assert((ALeftData<>0) and (ARightData<>0) and (ARightData<>1),Format('DoCompareData: Invalid Left %d or Right %d (data cannot be 0 neither 1)',[ALeftData,ARightData]));
+  if (ALeftData=ARightData) then begin
+    // Comparing same data because stored on same position
+    Exit(0);
+  end;
+  Assert(ALeftData<>ARightData,Format('DoCompareData: Left (%d) and Right (%d) are equals',[ALeftData,ARightData]));
+  if (ALeftData=1) then begin
+    if (FRight_Pos=0) or (FRight_Pos<>ARightData) then begin
+      if (FLeft_Pos=ARightData) then begin
+        Result := FOnCompareAbstractMemData(FSearchTarget,FLeft_Data);
+        Exit;
+      end;
+      FRight_Pos := ARightData;
+      FRight_Data := LoadData(ARightData);
+    end;
+    Result := FOnCompareAbstractMemData(FSearchTarget,FRight_Data);
+  end else begin
+    if (FLeft_Pos=0) or (FLeft_Pos<>ALeftData) then begin
+      if (FRight_Pos=ALeftData) then begin
+        // Use right as left
+        if (FLeft_Pos<>ARightData) then begin
+          // Left is not right, reload
+          FLeft_Pos := ARightData;
+          FLeft_Data := LoadData(ARightData);
+        end;
+        Result := FOnCompareAbstractMemData(FRight_Data,FLeft_Data);
+        Exit;
+      end;
+      FLeft_Pos := ALeftData;
+      FLeft_Data := LoadData(ALeftData);
+    end;
+    if (FRight_Pos=0) or (FRight_Pos<>ARightData) then begin
+      FRight_Pos := ARightData;
+      FRight_data := LoadData(ARightData);
+    end;
+    Result := FOnCompareAbstractMemData(FLeft_data,FRight_data);
+  end;
+end;
+
+function TAbstractMemBTreeData<TData>.FindData(const AData: TData;
+  var APosition: TAbstractMemPosition): Boolean;
+var Lnode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode;
+  LiPosNode : Integer;
+begin
+  FAbstractBTreeLock.Acquire;
+  try
+  FSearchTarget := AData;
+  ClearNode(Lnode);
+  if Find(1,Lnode,LiPosNode) then begin
+    APosition := Lnode.data[LiPosNode];
+    Result := True;
+  end else begin
+    // if Node exists will set APosition of previous value, otherwise will set 0
+    if Lnode.Count>LiPosNode then APosition := Lnode.data[LiPosNode]
+    else if Lnode.Count>0 then APosition := Lnode.data[Lnode.Count-1]
+    else APosition := 0;
+    Result := False;
+  end;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
+end;
+
+function TAbstractMemBTreeData<TData>.FindDataHighest(out AHighest: TData): Boolean;
+var Lpos : TAbstractMemPosition;
+begin
+  if FindHighest(Lpos) then begin
+    Result := True;
+    AHighest := LoadData(Lpos);
+  end else Result := False;
+end;
+
+function TAbstractMemBTreeData<TData>.FindDataLowest(out ALowest: TData): Boolean;
+var Lpos : TAbstractMemPosition;
+begin
+  if FindLowest(Lpos) then begin
+    Result := True;
+    ALowest := LoadData(Lpos);
+  end else Result := False;
+end;
+
+function TAbstractMemBTreeData<TData>.FindDataPrecessor(const AData: TData; var APrecessor: TData): Boolean;
+var Lnode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode;
+  LiPosNode : Integer;
+  Lpos : TAbstractMemPosition;
+begin
+  FAbstractBTreeLock.Acquire;
+  try
+  FSearchTarget := AData;
+  if Find(1,Lnode,LiPosNode) then begin
+    if FindPrecessor(Lnode.data[LiPosNode],Lpos) then begin
+      Result := True;
+      APrecessor := LoadData(Lpos);
+    end else Result := False;
+  end else Result := False;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
+end;
+
+function TAbstractMemBTreeData<TData>.FindDataSuccessor(const AData: TData; var ASuccessor: TData): Boolean;
+var Lnode : TAbstractBTree<TAbstractMemPosition,TAbstractMemPosition>.TAbstractBTreeNode;
+  LiPosNode : Integer;
+  Lpos : TAbstractMemPosition;
+begin
+  FAbstractBTreeLock.Acquire;
+  try
+  FSearchTarget := AData;
+  if Find(1,Lnode,LiPosNode) then begin
+    if FindSuccessor(Lnode.data[LiPosNode],Lpos) then begin
+      Result := True;
+      ASuccessor := LoadData(Lpos);
+    end else Result := False;
+  end else Result := False;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
+end;
+
+initialization
+
+finalization
+
+end.

+ 3 - 1
src/libraries/abstractmem/tests/AbstractMem.Tests.dpr

@@ -29,6 +29,7 @@ uses
   UAbstractAVLTree in '..\UAbstractAVLTree.pas',
   UAbstractBTree in '..\UAbstractBTree.pas',
   UAbstractMem in '..\UAbstractMem.pas',
+  UAbstractMemBTree in '..\UAbstractMemBTree.pas',
   UAbstractMemTList in '..\UAbstractMemTList.pas',
   UAVLCache in '..\UAVLCache.pas',
   UCacheMem in '..\UCacheMem.pas',
@@ -36,7 +37,8 @@ uses
   UOrderedList in '..\UOrderedList.pas',
   UCacheMem.Tests in 'src\UCacheMem.Tests.pas',
   UAbstractMem.Tests in 'src\UAbstractMem.Tests.pas',
-  UAbstractBTree.Tests in 'src\UAbstractBTree.Tests.pas';
+  UAbstractBTree.Tests in 'src\UAbstractBTree.Tests.pas',
+  UAbstractMemBTree.Tests in 'src\UAbstractMemBTree.Tests.pas';
 
 {$IF Defined(FPC) and (Defined(CONSOLE_TESTRUNNER))}
 type

+ 28 - 14
src/libraries/abstractmem/tests/src/UAbstractBTree.Tests.pas

@@ -16,6 +16,16 @@ uses
    UAbstractBTree, UOrderedList;
 
 type
+
+  TIntegerBTree = Class( TMemoryBTree<Integer> )
+  private
+  protected
+  public
+    constructor Create(AAllowDuplicates : Boolean; AOrder : Integer);
+    function NodeDataToString(const AData : Integer) : String; override;
+  End;
+
+
    TestTAbstractBTree = class(TTestCase)
    strict private
    public
@@ -37,11 +47,20 @@ type
 
 implementation
 
-function TComparison_XX_Integer(const ALeft, ARight: Integer): Integer;
+{ TIntegerBTree }
+
+constructor TIntegerBTree.Create(AAllowDuplicates: Boolean; AOrder: Integer);
+begin
+  inherited Create(TComparison_Integer,AAllowDuplicates,AOrder);
+end;
+
+function TIntegerBTree.NodeDataToString(const AData: Integer): String;
 begin
-  Result := ALeft - ARight;
+  Result := AData.ToString;
 end;
 
+{ TestTAbstractBTree }
+
 procedure TestTAbstractBTree.SetUp;
 begin
 end;
@@ -65,24 +84,22 @@ begin
   nDeletes := 0;
   Lbt := TIntegerBTree.Create(True,AOrder);
   try
+    Lbt.CircularProtection := (AOrder MOD 2)=0;
     repeat
       inc(nRounds);
       intValue := Random(AOrder * 100);
       if Random(2)=0 then begin
         if (Lbt.Add(intValue)) then begin
           inc(nAdds);
-          if Random(100)=0 then begin
-            Lbt.CheckConsistency;
-          end;
         end;
       end else begin
         if Lbt.Delete(intValue) then begin
           inc(nDeletes);
-          if Random(100)=0 then begin
-            Lbt.CheckConsistency;
-          end;
         end;
       end;
+      if Random(100)=0 then begin
+        Lbt.CheckConsistency;
+      end;
     until (nRounds>=AOrder * 10000);
     Lbt.CheckConsistency;
     // Delete mode
@@ -187,6 +204,7 @@ begin
   for Lorder := 3 to 7 do begin
     Lbt := TIntegerBTree.Create(False,Lorder);
     try
+      Lbt.CircularProtection := (Lorder MOD 2)=0;
       valMin := 1;
       intValue :=valMin;
       Lregs := 0;
@@ -229,6 +247,7 @@ begin
   for Lorder := 3 to 7 do begin
     Lbt := TIntegerBTree.Create(True,Lorder);
     try
+      Lbt.CircularProtection := (Lorder MOD 2)=0;
       valMin := 1;
       intValue :=valMin;
       Lregs := 0;
@@ -248,15 +267,12 @@ begin
         inc(i);
       end;
       Assert(intValue=valMax,Format('Successor %d<>%d',[intValue,valMax]));
-//      Assert(i=Lregs,Format('Succcessor count %d %d',[i,Lregs]));
       Lbt.FindHighest(intValue);
       i := 1;
       while (Lbt.FindPrecessor(intValue,intValue)) do begin
         inc(i);
       end;
       Assert(intValue=valMin,Format('Precessor %d<>%d',[intValue,valMin]));
-//      Assert(i=Lregs,Format('Precessor count %d %d',[i,Lregs]));
-
     finally
       Lbt.Free;
     end;
@@ -303,7 +319,7 @@ begin
       i :=1;
       while Lbt.Height<Lorder+1 do begin
         intValue := Random(100);
-        DoInsert(intValue); // Lbt.Add(intValue);
+        DoInsert(intValue);
         inc(i);
       end;
 
@@ -320,7 +336,6 @@ begin
       end;
       LCurrentTree := Lbt.BTreeToString;
       Lbt.CheckConsistency;
-      if LLastTree = '' then Beep;
     finally
       Lbt.Free;
     end;
@@ -375,7 +390,6 @@ begin
         intValue := Random(intValue)+1;
         DoDelete(intValue);
       end;
-      if LLastTree = '' then Beep;
     finally
       Lbt.Free;
     end;

+ 346 - 0
src/libraries/abstractmem/tests/src/UAbstractMemBTree.Tests.pas

@@ -0,0 +1,346 @@
+unit UAbstractMemBTree.Tests;
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+interface
+
+uses
+   SysUtils,
+   {$IFDEF FPC}
+   fpcunit, testutils, testregistry,
+   {$ELSE}
+   TestFramework,
+   {$ENDIF}
+   {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults,{$ELSE}Generics.Collections,Generics.Defaults,{$ENDIF}
+   UAbstractMem, UAbstractBTree.Tests,
+   UAbstractBTree, UOrderedList, UAbstractMemBTree;
+
+type
+   TAbstractMemBTreeExampleInteger = Class(TAbstractMemBTree)
+   protected
+     procedure DisposeData(var AData : TAbstractMemPosition); override;
+     function DoCompareData(const ALeftData, ARightData: TAbstractMemPosition): Integer; override;
+   public
+     function NodeDataToString(const AData : TAbstractMemPosition) : String; override;
+   End;
+
+   TAbstractMemBTreeExampleString = Class(TAbstractMemBTreeData<String>)
+   protected
+     function LoadData(const APosition : TAbstractMemPosition) : String; override;
+     function SaveData(const AData : String) : TAMZone; override;
+   public
+     function NodeDataToString(const AData : TAbstractMemPosition) : String; override;
+   End;
+
+   TestTAbstractMemBTree = class(TTestCase)
+   strict private
+   public
+     procedure SetUp; override;
+     procedure TearDown; override;
+     procedure TestInfinite_Integer(AOrder : Integer; AAllowDuplicates : Boolean);
+     procedure TestInfinite_String(AOrder : Integer; AAllowDuplicates : Boolean);
+     procedure TestInfinite(AOrder : Integer);
+     procedure DoCheckAbstractMem(AAbstractMem : TAbstractMem; AUsedBytes : Integer);
+   published
+     procedure TestInfiniteOrder_3;
+     procedure TestInfiniteOrder_4;
+     procedure TestInfiniteOrder_5;
+     procedure TestInfiniteOrder_6;
+     procedure TestInfiniteOrder_7;
+   end;
+
+implementation
+
+{ TAbstractMemBTreeExampleInteger }
+
+procedure TAbstractMemBTreeExampleInteger.DisposeData(var AData: TAbstractMemPosition);
+begin
+  // NOTE: Nothing to do NEITHER to inherit from ancestor
+end;
+
+function TAbstractMemBTreeExampleInteger.DoCompareData(const ALeftData, ARightData: TAbstractMemPosition): Integer;
+begin
+  Result := ALeftData - ARightData;
+end;
+
+function TAbstractMemBTreeExampleInteger.NodeDataToString(const AData: TAbstractMemPosition): String;
+begin
+  Result := IntToStr(AData);
+end;
+
+{ TAbstractMemBTreeExampleString }
+
+function TAbstractMemBTreeExampleString.LoadData(const APosition: TAbstractMemPosition): String;
+var i : Integer;
+  wLength : Word;
+  Lbuff : TBytes;
+begin
+  Result := '';
+  wLength := 0;
+  FAbstractMem.Read(APosition,wLength,2);
+  if wLength<=0 then Exit;
+  SetLength(Lbuff,wLength);
+  FAbstractMem.Read(APosition+2,LBuff[0],wLength);
+  for i:=0 to wLength-1 do begin
+    Result := Result + Char(LBuff[i]);
+  end;
+end;
+
+function TAbstractMemBTreeExampleString.NodeDataToString(const AData: TAbstractMemPosition): String;
+begin
+  Result := LoadData(AData);
+end;
+
+function TAbstractMemBTreeExampleString.SaveData(const AData: String): TAMZone;
+var i : Integer;
+  wLength : Word;
+  Lbuff : TBytes;
+begin
+  wLength := Length(AData);
+  Result := FAbstractMem.New( wLength+2 );
+  SetLength(Lbuff,wLength+2);
+  Move(wLength,Lbuff[0],2);
+  for i:=0 to AData.Length-1 do begin
+    Lbuff[2 + i] := Byte(Char(AData.Chars[i]));
+  end;
+  FAbstractMem.Write(Result.position,Lbuff[0],Length(Lbuff));
+end;
+
+{ TestTAbstractMemBTree }
+
+procedure TestTAbstractMemBTree.DoCheckAbstractMem(AAbstractMem: TAbstractMem; AUsedBytes: Integer);
+var
+  LTotalUsedSize, LTotalUsedBlocksCount, LTotalLeaksSize, LTotalLeaksBlocksCount : Integer;
+begin
+  Assert(AAbstractMem.CheckConsistency(Nil,LTotalUsedSize, LTotalUsedBlocksCount, LTotalLeaksSize, LTotalLeaksBlocksCount));
+  Assert(LTotalUsedSize=AUsedBytes,Format('Total used %d bytes (%d blocks) different from expected %d bytes - Total free %d bytes (%d blocks)',[LTotalUsedSize, AUsedBytes, LTotalUsedBlocksCount, LTotalLeaksSize, LTotalLeaksBlocksCount]));
+end;
+
+procedure TestTAbstractMemBTree.SetUp;
+begin
+end;
+
+procedure TestTAbstractMemBTree.TearDown;
+begin
+end;
+
+procedure TestTAbstractMemBTree.TestInfinite(AOrder: Integer);
+begin
+  TestInfinite_Integer(AOrder,(AOrder MOD 2)=0);
+  TestInfinite_String(AOrder,(AOrder MOD 2)=0);
+end;
+
+procedure TestTAbstractMemBTree.TestInfinite_Integer(AOrder : Integer; AAllowDuplicates : Boolean);
+var Lbt : TAbstractMemBTreeExampleInteger;
+  Lbts : TAbstractMemBTreeExampleString;
+  Lzone : TAMZone;
+  intValue, nRounds, nAdds, nDeletes, i, j : Integer;
+  Lnode : TIntegerBTree.TAbstractBTreeNode;
+  Lmem : TAbstractMem;
+  LCurr : String;
+begin
+  Lmem := TMem.Create(0,False);
+  Try
+    {$IFDEF FPC}
+    Randomize;
+    {$ELSE}
+    RandomizeProc(0);
+    {$ENDIF}
+    nRounds := 0;
+    nAdds := 0;
+    nDeletes := 0;
+    Lzone := Lmem.New(TAbstractMemBTree.MinAbstractMemInitialPositionSize);
+    try
+    Lbt := TAbstractMemBTreeExampleInteger.Create(Lmem,Lzone,AAllowDuplicates,AOrder);
+    try
+      repeat
+        inc(nRounds);
+        intValue := Random(AOrder * 100);
+        if Random(2)=0 then begin
+          if (Lbt.Add(intValue)) then begin
+            inc(nAdds);
+          end;
+        end else begin
+          if Lbt.Delete(intValue) then begin
+            inc(nDeletes);
+          end;
+        end;
+        if Random(100)=0 then begin
+          Lbt.CheckConsistency;
+        end;
+      until (nRounds>=AOrder * 10000);
+      Lbt.CheckConsistency;
+      // Delete mode
+      while Lbt.Count>0 do begin
+        Lnode := Lbt.Root;
+        while (Not Lnode.IsLeaf) and (Random(5)>0) do begin
+          Lnode := Lbt.GetNode(Lnode.childs[Random(Lnode.Count)+1]);
+        end;
+        If Not Lbt.Delete(Lnode.data[Random(Lnode.Count)]) then raise Exception.Create('Not Found to delete!');
+        if Random(100)=0 then begin
+          Lbt.CheckConsistency;
+        end;
+      end;
+      Lbt.CheckConsistency;
+      // Try to re-use
+      for i := 1 to AOrder do begin
+        intValue := Random(AOrder * 100);
+        Assert(Lbt.Add(intValue),Format('Cannot re-use %d/%d and add %d',[i,AOrder,intValue]));
+        Lbt.CheckConsistency;
+        Assert(Lbt.FindIndex(i-1,j),Format('Cannot find %d on index %d on order %d',[intValue,i-1,AOrder]));
+        Assert(Not Lbt.FindIndex(i,j),Format('Found %d on index %d on order %d',[j,i-1,AOrder]));
+      end;
+    finally
+      Lbt.Free;
+    end;
+    Lbt := TAbstractMemBTreeExampleInteger.Create(Lmem,Lzone,AAllowDuplicates,AOrder);
+    try
+      Lbt.CheckConsistency;
+      Lbt.EraseTree;
+      Lbt.CheckConsistency;
+    finally
+      Lbt.Free;
+    end;
+    finally
+      Lmem.Dispose(Lzone);
+    end;
+    DoCheckAbstractMem(Lmem,0);
+  Finally
+    Lmem.Free;
+  End;
+end;
+
+procedure TestTAbstractMemBTree.TestInfinite_String(AOrder: Integer; AAllowDuplicates : Boolean);
+var Lbt : TAbstractMemBTreeExampleString;
+  Lzone : TAMZone;
+  intValue, nRounds, nAdds, nDeletes, i : Integer;
+  Lnode : TIntegerBTree.TAbstractBTreeNode;
+  Lmem : TAbstractMem;
+  LCurr : String;
+  LCurrData : String;
+begin
+  Lmem := TMem.Create(0,False);
+  Try
+    {$IFDEF FPC}
+    Randomize;
+    {$ELSE}
+    RandomizeProc(0);
+    {$ENDIF}
+    nRounds := 0;
+    nAdds := 0;
+    nDeletes := 0;
+    Lzone := Lmem.New(TAbstractMemBTree.MinAbstractMemInitialPositionSize);
+    try
+    Lbt := TAbstractMemBTreeExampleString.Create(Lmem,Lzone,AAllowDuplicates,AOrder,TComparison_String);
+    try
+      repeat
+        inc(nRounds);
+        intValue := Random(AOrder * 100);
+        if Random(2)=0 then begin
+          if (Lbt.AddData(intValue.ToString)) then begin
+            inc(nAdds);
+          end;
+        end else begin
+          if Lbt.DeleteData(intValue.ToString) then begin
+            inc(nDeletes);
+          end;
+        end;
+        if Random(100)=0 then begin
+          Lbt.CheckConsistency;
+        end;
+      until (nRounds>=AOrder * 10000);
+      Lbt.CheckConsistency;
+      // Delete mode
+      while Lbt.Count>0 do begin
+        Lnode := Lbt.Root;
+        while (Not Lnode.IsLeaf) and (Random(5)>0) do begin
+          Lnode := Lbt.GetNode(Lnode.childs[Random(Lnode.Count)+1]);
+        end;
+        LCurrData := Lbt.LoadData(Lnode.data[Random(Lnode.Count)]);
+        if Not Lbt.DeleteData(LCurrData) then raise EAbstractMemBTree.Create('Not found to delete!');
+        if Random(100)=0 then begin
+          Lbt.CheckConsistency;
+        end;
+      end;
+      Lbt.CheckConsistency;
+      // Try to re-use
+      for i := 1 to AOrder do begin
+        intValue := i;
+        Assert(Lbt.AddData(intValue.ToString),Format('Cannot re-use %d/%d and add %d',[i,AOrder,intValue]));
+        Lbt.CheckConsistency;
+      end;
+    finally
+      Lbt.Free;
+    end;
+    Lbt := TAbstractMemBTreeExampleString.Create(Lmem,Lzone,AAllowDuplicates,AOrder,TComparison_String);
+    try
+      Lbt.CheckConsistency;
+      LCurr := Lbt.BTreeToString;
+      // SUCCESSOR
+      Assert(Lbt.FindDataLowest(LCurrData),'Not found Lowest');
+      Assert(LcurrData='1','Not valid lowest');
+      for i := 1 to AOrder do begin
+        Assert(i.ToString=LcurrData,Format('Not valid successor %d %s',[i,LcurrData]));
+        if i<AOrder then begin
+          Assert(Lbt.FindDataSuccessor(LcurrData,LCurrData),Format('Not found successor %d %s',[i,LcurrData]));
+        end else begin
+          Assert(Not Lbt.FindDataSuccessor(LCurrData,LCurrData),Format('Not valid last successor %s',[LCurrData]));
+        end;
+      end;
+      // PRECESSOR
+      Assert(Lbt.FindDataHighest(LCurrData),'Not found Highest');
+      Assert(LcurrData=IntToStr(AOrder),'Not valid highest');
+      for i := AOrder downto 1 do begin
+        Assert(i.ToString=LcurrData,Format('Not valid precessor %d %s',[i,LcurrData]));
+        if i>1 then begin
+          Assert(Lbt.FindDataPrecessor(LcurrData,LCurrData),Format('Not found precessor %d %s',[i,LcurrData]));
+        end else begin
+          Assert(Not Lbt.FindDataPrecessor(LCurrData,LCurrData),Format('Not valid last precessor %s',[LCurrData]));
+        end;
+      end;
+      Lbt.EraseTree;
+      Assert(Lbt.Count=0,'Not erased tree count 0');
+      Lbt.CheckConsistency;
+      Lbt.EraseTree;
+    finally
+      Lbt.Free;
+    end;
+    finally
+      Lmem.Dispose(Lzone);
+    end;
+    DoCheckAbstractMem(Lmem,0);
+  Finally
+    Lmem.Free;
+  End;
+end;
+
+procedure TestTAbstractMemBTree.TestInfiniteOrder_3;
+begin
+  TestInfinite(3);
+end;
+
+procedure TestTAbstractMemBTree.TestInfiniteOrder_4;
+begin
+  TestInfinite(4);
+end;
+
+procedure TestTAbstractMemBTree.TestInfiniteOrder_5;
+begin
+  TestInfinite(5);
+end;
+
+procedure TestTAbstractMemBTree.TestInfiniteOrder_6;
+begin
+  TestInfinite(6);
+end;
+
+procedure TestTAbstractMemBTree.TestInfiniteOrder_7;
+begin
+  TestInfinite(7);
+end;
+
+initialization
+  RegisterTest(TestTAbstractMemBTree{$IFNDEF FPC}.Suite{$ENDIF});
+end.

+ 21 - 0
src/libraries/pascalcoin/UAppParams.pas

@@ -57,6 +57,7 @@ Type
     Procedure SetAsCardinal(CardValue : Cardinal);
     Procedure SetAsString(StringValue : String);
     Procedure SetAsInt64(Int64Value : Int64);
+    Procedure SetAsUInt64(UInt64Value : UInt64);
     Procedure SetAsBoolean(BoolValue : Boolean);
     Procedure SetAsStream(Stream : TStream);
     Procedure SetAsTBytes(Bytes : TBytes);
@@ -65,6 +66,7 @@ Type
     function GetAsBoolean(Const DefValue : Boolean): Boolean;
     function GetAsInteger(Const DefValue : Integer): Integer;
     function GetAsInt64(Const DefValue : Int64): Int64;
+    function GetAsUInt64(Const DefValue : UInt64): UInt64;
     function GetAsStream(Stream : TStream) : Integer;
     function GetAsTBytes(Const DefValue : TBytes) : TBytes;
   End;
@@ -186,6 +188,18 @@ begin
   end;
 end;
 
+function TAppParam.GetAsUInt64(const DefValue: UInt64): UInt64;
+begin
+  if IsNull then Result := DefValue
+  else begin
+    Try
+      Result := FValue;
+    Except
+      Result := DefValue;
+    End;
+  end;
+end;
+
 function TAppParam.GetAsInteger(const DefValue: Integer): Integer;
 begin
   if IsNull then Result := DefValue
@@ -357,6 +371,13 @@ begin
   If Assigned(FAppParams) then FAppParams.Save;
 end;
 
+procedure TAppParam.SetAsUInt64(UInt64Value: UInt64);
+begin
+  FParamType := ptInt64;
+  FValue := UInt64Value;
+  If Assigned(FAppParams) then FAppParams.Save;
+end;
+
 procedure TAppParam.SetAsInteger(IntValue: Integer);
 begin
   FParamType := ptInteger;

+ 20 - 0
src/libraries/pascalcoin/UJSONFunctions.pas

@@ -76,6 +76,7 @@ Type
     Function ToJSONFormatted(pretty:Boolean;const prefix : String) : String; override;
   public
     Constructor Create; override;
+    Constructor CreateFromVariant(const Value: Variant);
     Constructor CreateFromJSONValue(JSONValue : TJSONValue);
     Property Value : Variant read FValue write SetValue;
     Function AsString(DefValue : String) : String;
@@ -158,6 +159,8 @@ Type
     Destructor Destroy; override;
     Function FindName(Name : String) : TPCJSONNameValue;
     Function IndexOfName(Name : String) : Integer;
+    Function HasName(Name: String): Boolean;
+    Function HasValue(const AParamName : String) : Boolean;
     Procedure DeleteName(Name : String);
     Function GetAsVariant(Name : String) : TPCJSONVariantValue;
     Function GetAsObject(Name : String) : TPCJSONObject;
@@ -496,6 +499,12 @@ begin
   FWritable := False;
 end;
 
+Constructor TPCJSONVariantValue.CreateFromVariant(const Value: Variant);
+begin
+  Create;
+  SetValue(Value);
+end;
+
 constructor TPCJSONVariantValue.CreateFromJSONValue(JSONValue: TJSONValue);
 {$IFnDEF FPC}
 Var d : Double;
@@ -878,6 +887,17 @@ begin
   Result := -1;
 end;
 
+function TPCJSONObject.HasName(Name: String): Boolean;
+begin
+  Result := IndexOfName(Name) >= 0;
+end;
+
+Function TPCJSONObject.HasValue(const AParamName : String) : Boolean;
+begin
+  Result := HasName(AParamName) AND (NOT AsString(AParamName, String.Empty).IsEmpty);
+end;
+
+
 function TPCJSONObject.LoadAsStream(ParamName: String; Stream: TStream): Integer;
 Var s : RawByteString;
 begin

+ 9 - 0
src/libraries/sphere10/UCommon.pas

@@ -43,6 +43,15 @@ const
   MaxSeconds = MaxMilliseconds div 60;
   MinSeconds = MinMilliseconds div 60;
 
+  BYTE_BIT_0 = byte(1);
+  BYTE_BIT_1 = byte(2);
+  BYTE_BIT_2 = byte(4);
+  BYTE_BIT_3 = byte(8);
+  BYTE_BIT_4 = byte(16);
+  BYTE_BIT_5 = byte(32);
+  BYTE_BIT_6 = byte(64);
+  BYTE_BIT_7 = byte(128);
+
 { GLOBAL HELPER FUNCTIONS }
 
 

+ 4 - 0
src/pascalcoin_daemon.ini

@@ -63,3 +63,7 @@ ABSTRACTMEM_CACHE_MAX_ACCOUNTS=
 ;ABSTRACTMEM_CACHE_MAX_PUBKEYS : Integer
 ;Max number of public keys to store at cache - Default 5000
 ABSTRACTMEM_CACHE_MAX_PUBKEYS=
+
+;MAX_PAYTOKEY_MOLINAS : Integer
+;Max MOLINAS (1 MOLINA = 0.0001 PASC) can be paid for a PayToKey operation using EPaAsa format (Default=5000=0.5 PASC)
+MAX_PAYTOKEY_MOLINAS=5000

+ 1 - 1
src/pascalcoin_daemon.lpi

@@ -53,7 +53,7 @@
     </Target>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
-      <OtherUnitFiles Value="core;libraries\synapse;libraries\pascalcoin;libraries\generics.collections;libraries\sphere10;libraries\hashlib4pascal;libraries\paszlib;libraries\cryptolib4pascal;libraries\simplebaselib4pascal;libraries\abstractmem;libraries\regex"/>
+      <OtherUnitFiles Value="core;libraries\synapse;libraries\pascalcoin;libraries\sphere10;libraries\hashlib4pascal;libraries\paszlib;libraries\cryptolib4pascal;libraries\simplebaselib4pascal;libraries\abstractmem;libraries\regex"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
     <CodeGeneration>

+ 13 - 5
src/pascalcoin_miner.lpi

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <CONFIG>
   <ProjectOptions>
-    <Version Value="10"/>
+    <Version Value="11"/>
     <PathDelim Value="\"/>
     <General>
       <Flags>
@@ -9,7 +9,7 @@
         <MainUnitHasTitleStatement Value="False"/>
       </Flags>
       <SessionStorage Value="InProjectDir"/>
-      <MainUnit Value="0"/>      
+      <MainUnit Value="0"/>
       <UseAppBundle Value="False"/>
       <ResourceType Value="res"/>
     </General>
@@ -24,9 +24,16 @@
     </PublishOptions>
     <RunParams>
       <local>
-        <FormatVersion Value="1"/>
         <CommandLineParams Value="-c 1 -s -n TEST"/>
       </local>
+      <FormatVersion Value="2"/>
+      <Modes Count="1">
+        <Mode0 Name="default">
+          <local>
+            <CommandLineParams Value="-c 1 -s -n TEST"/>
+          </local>
+        </Mode0>
+      </Modes>
     </RunParams>
     <RequiredPackages Count="1">
       <Item1>
@@ -36,7 +43,8 @@
     <Units Count="1">
       <Unit0>
         <Filename Value="pascalcoin_miner.pp"/>
-        <IsPartOfProject Value="True"/>        
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="PascalCoinMiner"/>
       </Unit0>
     </Units>
   </ProjectOptions>
@@ -48,7 +56,7 @@
     </Target>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
-      <OtherUnitFiles Value="core;libraries\pasopencl;libraries\synapse;libraries\sphere10;libraries\hashlib4pascal;libraries\generics.collections;libraries\pascalcoin;libraries\paszlib"/>
+      <OtherUnitFiles Value="core;libraries\pasopencl;libraries\synapse;libraries\sphere10;libraries\hashlib4pascal;libraries\pascalcoin;libraries\paszlib;libraries\regex;libraries\abstractmem;libraries\cryptolib4pascal;libraries\simplebaselib4pascal"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
     <CodeGeneration>

+ 31 - 13
src/pascalcoin_miner.pp

@@ -27,8 +27,7 @@ uses
   UBlockChain, UPoolMinerThreads, UGPUMining,
   UPoolMining, ULog, UThread, UAccounts, UCrypto,
   UConst, UTime, UNode, UNetProtocol, USha256,
-  UOpenSSL, UBaseTypes, UCommon,
-  DelphiCL;
+  UOpenSSL, UBaseTypes, UCommon, DelphiCL;
 
 type
 
@@ -44,6 +43,7 @@ type
     procedure WriteLine(nline : Integer; txt : String);
     procedure OnInThreadNewLog(logtype : TLogType; Time : TDateTime; ThreadID : TThreadID; Const sender, logtext : AnsiString);
   protected
+    FOutputAsLogs : Boolean;
     FWindow32X1,FWindow32Y1,FWindow32X2,FWindow32Y2: DWord;
     FLock : TCriticalSection;
     FPoolMinerThread : TPoolMinerThread;
@@ -57,7 +57,7 @@ type
   end;
 
 Const
-  CT_MINER_VERSION = {$IFDEF PRODUCTION}'5.1'{$ELSE}{$IFDEF TESTNET}'5.1 TESTNET'{$ELSE}ERROR{$ENDIF}{$ENDIF};
+  CT_MINER_VERSION = {$IFDEF PRODUCTION}'5.4'{$ELSE}{$IFDEF TESTNET}'5.4 TESTNET'{$ELSE}ERROR{$ENDIF}{$ENDIF};
   CT_Line_DeviceStatus = 3;
   CT_Line_ConnectionStatus = 4;
   CT_Line_MinerValues = 7;
@@ -100,7 +100,6 @@ begin
 end;
 
 procedure TPascalMinerApp.OnConnectionStateChanged(Sender: TObject);
-Const CT_state : Array[boolean] of String = ('Disconnected','Connected');
 var i : Integer;
   s : String;
 begin
@@ -120,8 +119,6 @@ begin
 end;
 
 procedure TPascalMinerApp.OnDeviceStateChanged(Sender: TObject);
-Var i : Integer;
-  s : String;
 begin
   If Sender is TCustomMinerDeviceThread then begin
     If TCustomMinerDeviceThread(Sender).IsMining then WriteLine(CT_Line_DeviceStatus,'') // clear line
@@ -153,6 +150,10 @@ Var i : Integer;
 begin
   FLock.Acquire;
   try
+    if FOutputAsLogs then begin
+      WriteLn(Format('%s %s',[FormatDateTime('hh:nn:ss.zzz',Now()),txt]));
+      Exit;
+    end;
     i := length(txt);
     if i<=(FWindow32X2-FWindow32X1+1) then begin
       setlength(txt,FWindow32X2-FWindow32X1+1);
@@ -280,9 +281,9 @@ var
   end;
 
   Procedure DoWaitAndLog;
-  Var tc : TTickCount;
+  Var tc, LMaxElapsed : TTickCount;
     gs,ms : TMinerStats;
-    hrReal,hrHashing, glhrHashing, glhrReal : Real;
+    hrHashing  : Real;
     i : Integer;
     devt : TCustomMinerDeviceThread;
     s : String;
@@ -297,7 +298,9 @@ var
       end;
       while (Not Terminated) do begin
         sleep(100);
-        If TPlatform.GetElapsedMilliseconds(tc)>1000 then begin
+        if FOutputAsLogs then LMaxElapsed := 10000
+        else LMaxElapsed:=1000;
+        If TPlatform.GetElapsedMilliseconds(tc)>LMaxElapsed then begin
           tc := TPlatform.GetTickCount;
           For i:=0 to FDeviceThreads.Count-1 do begin
             devt := TCustomMinerDeviceThread(FDeviceThreads[i]);
@@ -307,7 +310,7 @@ var
               else hrHashing := 0;
               gs := devt.GlobalDeviceStats;
               If ms.RoundsCount>0 then begin
-                s := FormatDateTime('hh:nn:ss',now)+Format(' Miner:"%s" at %0.1f H/s - Rounds: %0.2f K Found: %d',[devt.MinerValuesForWork.payload_start.ToString,hrHashing, gs.RoundsCount/1000, gs.WinsCount]);
+                s := FormatDateTime('hh:nn:ss',now)+Format(' Miner:"%s" at %0.1f H/s (%d %0.2f) - Rounds: %0.2f K Found: %d',[devt.MinerValuesForWork.payload_start.ToString,hrHashing, ms.InternalComputingRounds, ms.RoundsCount/1000, gs.RoundsCount/1000, gs.WinsCount]);
                 If (gs.Invalids>0) then s := s +' '+inttostr(gs.Invalids)+' ERRORS!';
                 WriteLine(CT_Line_MiningStatus+i,s);
               end else begin
@@ -427,7 +430,7 @@ begin
   FLock := TCriticalSection.Create;
   Try
     // quick check parameters
-    ErrorMsg:=CheckOptions('hp:d:s::c:n::t:u::x::l::', 'help platform device server cpu minername testmode user pwd logfile logall');
+    ErrorMsg:=CheckOptions('hp:d:s::c:n::t:u::x::l::', 'help platform: device: server: cpu: minername: testmode: user: pwd: logfile: logall: outputlogs:');
     if ErrorMsg<>'' then begin
       //ShowException(Exception.Create(ErrorMsg));
       WriteLn(ErrorMsg);
@@ -440,10 +443,19 @@ begin
       Exit;
     end;
 
-    if (Not HasOption('p','platform')) And (Not HasOption('d','device')) And (Not HasOption('c','cpu:')) then begin
+    if HasOption('outputlogs') then begin
+      s := Trim(GetOptionValue('outputlogs'));
+      if (s='0') or (s='false') then FOutputAsLogs:=False
+      else if (s='1') or (s='-1') or (s='true') then FOutputAsLogs:=True
+      else begin
+        Writeln('Invalid outputlogs argument "'+s+'"');
+        Exit;
+      end;
+    end;
+
+    if (Not HasOption('p','platform')) And (Not HasOption('d','device')) And (Not HasOption('c','cpu')) then begin
       Writeln('Need to specify -p X and -d Y for GPU mining or -c N for CPU mining');
       Writeln('Execute ',ExtractFileName(ExeName),' -h for more info');
-      ShowGPUDrivers;
       Exit;
     end;
 
@@ -511,6 +523,11 @@ begin
   FDeviceThreads := TList.Create;
   StopOnException:=True;
   FAppStartTime := Now;
+  {$IF Defined(WINDOWS)}
+  FOutputAsLogs := False;
+  {$ELSE}
+  FOutputAsLogs := True;
+  {$ENDIF}
 end;
 
 destructor TPascalMinerApp.Destroy;
@@ -537,6 +554,7 @@ begin
   writeln('  (Not needed for PascalCoin core, only some third party pools)');
   writeln('  -u USERNAME');
   writeln('  -x PASSWORD');
+  writeln('  --outputlogs=BOOLEAN (Show output style as a log)');
   writeln('');
   writeln('Basic example GPU mining over multiple devices: ');
   writeln('  ',ExtractFileName(ExeName),' -p 0 -d 0,1,2,3 -s -n ABC');

+ 51 - 32
src/pascalcoin_wallet_classic.dpr

@@ -15,25 +15,73 @@ uses
   Interfaces,
   {$ENDIF }
   Forms,
+  UFRMAbout in 'gui-classic\UFRMAbout.pas' {FRMAbout},
+  UFRMAccountSelect in 'gui-classic\UFRMAccountSelect.pas' {FRMAccountSelect},
+  UFRMMemoText in 'gui-classic\UFRMMemoText.pas' {FRMMemoText},
+  UFRMNewPrivateKeyType in 'gui-classic\UFRMNewPrivateKeyType.pas' {FRMNewPrivateKeyType},
+  UFRMNodesIp in 'gui-classic\UFRMNodesIp.pas' {FRMNodesIp},
+  UFRMOperation in 'gui-classic\UFRMOperation.pas' {FRMOperation},
+  UFRMOperationsExplorer in 'gui-classic\UFRMOperationsExplorer.pas' {FRMOperationsExplorer},
+  {$IFDEF USE_GNUGETTEXT}
+  UFRMSelectLanguage in 'gui-classic\UFRMSelectLanguage.pas' {FRMChangeLanguage},
+  {$ENDIF }
+  UFRMPascalCoinWalletConfig in 'gui-classic\UFRMPascalCoinWalletConfig.pas' {FRMPascalCoinWalletConfig},
+  UFRMPayloadDecoder in 'gui-classic\UFRMPayloadDecoder.pas' {FRMPayloadDecoder},
+  UFRMRandomOperations in 'gui-classic\UFRMRandomOperations.pas' {FRMRandomOperations},
+  UFRMRPCCalls in 'gui-classic\UFRMRPCCalls.pas' {FRMRPCCalls},
+  UFRMWallet in 'gui-classic\UFRMWallet.pas' {FRMWallet},
+  UFRMWalletKeys in 'gui-classic\UFRMWalletKeys.pas' {FRMWalletKeys},
+  UGridUtils in 'gui-classic\UGridUtils.pas',
+  UGUIUtils in 'gui-classic\UGUIUtils.pas',
+  UFRMHashLock in 'gui-classic\UFRMHashLock.pas' {FRMHashLock},
+  UFRMDiagnosticTool in 'gui-classic\UFRMDiagnosticTool.pas' {FRMDiagnosticTool},
+  UCommon in 'libraries\sphere10\UCommon.pas',
+  UMemory in 'libraries\sphere10\UMemory.pas',
   UAccountKeyStorage in 'core\UAccountKeyStorage.pas',
   UAccounts in 'core\UAccounts.pas',
-  {$IFDEF Use_OpenSSL}
   UAES in 'core\UAES.pas',
-  {$ENDIF }
   UBaseTypes in 'core\UBaseTypes.pas',
   UBlockChain in 'core\UBlockChain.pas',
   UChunk in 'core\UChunk.pas',
   UConst in 'core\UConst.pas',
   UCrypto in 'core\UCrypto.pas',
+  UECIES in 'core\UECIES.pas',
+  UEncoding in 'core\UEncoding.pas',
+  UEPasa in 'core\UEPasa.pas',
   UFileStorage in 'core\UFileStorage.pas',
   ULog in 'core\ULog.pas',
+  UMurMur3Fast in 'core\UMurMur3Fast.pas',
   UNetProtection in 'core\UNetProtection.pas',
   UNetProtocol in 'core\UNetProtocol.pas',
   UNode in 'core\UNode.pas',
+  UOpenSSL in 'core\UOpenSSL.pas',
   UOpTransaction in 'core\UOpTransaction.pas',
+  {$IFDEF USE_ABSTRACTMEM}
+  UPCAbstractMem in 'core\UPCAbstractMem.pas',
+  UPCAbstractMemAccountKeys in 'core\UPCAbstractMemAccountKeys.pas',
+  {$ENDIF}
+  UPCAbstractMemAccounts in 'core\UPCAbstractMemAccounts.pas',
+  UPCAccountsOrdenations in 'core\UPCAccountsOrdenations.pas',
+  UPCCryptoLib4Pascal in 'core\UPCCryptoLib4Pascal.pas',
+  UPCDataTypes in 'core\UPCDataTypes.pas',
+  UPCEncryption in 'core\UPCEncryption.pas',
+  UPCHardcodedRandomHashTable in 'core\UPCHardcodedRandomHashTable.pas',
+  UPCOperationsBlockValidator in 'core\UPCOperationsBlockValidator.pas',
+  UPCOperationsSignatureValidator in 'core\UPCOperationsSignatureValidator.pas',
+  UPCOrderedLists in 'core\UPCOrderedLists.pas',
+  UPCRPCFileUtils in 'core\UPCRPCFileUtils.pas',
+  UPCRPCFindAccounts in 'core\UPCRPCFindAccounts.pas',
+  UPCRPCFindBlocks in 'core\UPCRPCFindBlocks.pas',
+  UPCRPCOpData in 'core\UPCRPCOpData.pas',
+  UPCRPCSend in 'core\UPCRPCSend.pas',
+  UPCSafeBoxRootHash in 'core\UPCSafeBoxRootHash.pas',
+  UPCTemporalAbstractMem in 'core\UPCTemporalAbstractMem.pas',
+  UPCTemporalFileStream in 'core\UPCTemporalFileStream.pas',
+  UPCTNetDataExtraMessages in 'core\UPCTNetDataExtraMessages.pas',
   UPoolMinerThreads in 'core\UPoolMinerThreads.pas',
   UPoolMining in 'core\UPoolMining.pas',
   URandomHash in 'core\URandomHash.pas',
+  URandomHash2 in 'core\URandomHash2.pas',
   URPC in 'core\URPC.pas',
   USettings in 'core\USettings.pas',
   USha256 in 'core\USha256.pas',
@@ -41,36 +89,7 @@ uses
   UThread in 'core\UThread.pas',
   UTime in 'core\UTime.pas',
   UTxMultiOperation in 'core\UTxMultiOperation.pas',
-  UWallet in 'core\UWallet.pas',
-  UFRMAbout in 'gui-classic\UFRMAbout.pas' {FRMAbout},
-  UFRMAccountSelect in 'gui-classic\UFRMAccountSelect.pas' {FRMAccountSelect},
-  UFRMMemoText in 'gui-classic\UFRMMemoText.pas' {FRMMemoText},
-  UFRMNewPrivateKeyType in 'gui-classic\UFRMNewPrivateKeyType.pas' {FRMNewPrivateKeyType},
-  UFRMNodesIp in 'gui-classic\UFRMNodesIp.pas' {FRMNodesIp},
-  UFRMOperation in 'gui-classic\UFRMOperation.pas' {FRMOperation},
-  UFRMOperationsExplorer in 'gui-classic\UFRMOperationsExplorer.pas' {FRMOperationsExplorer},
-  {$IFDEF USE_GNUGETTEXT}
-  UFRMSelectLanguage in 'gui-classic\UFRMSelectLanguage.pas' {FRMChangeLanguage},
-  {$ENDIF }
-  UFRMPascalCoinWalletConfig in 'gui-classic\UFRMPascalCoinWalletConfig.pas' {FRMPascalCoinWalletConfig},
-  UFRMPayloadDecoder in 'gui-classic\UFRMPayloadDecoder.pas' {FRMPayloadDecoder},
-  UFRMRandomOperations in 'gui-classic\UFRMRandomOperations.pas' {FRMRandomOperations},
-  UFRMRPCCalls in 'gui-classic\UFRMRPCCalls.pas' {FRMRPCCalls},
-  UFRMWallet in 'gui-classic\UFRMWallet.pas' {FRMWallet},
-  UFRMWalletKeys in 'gui-classic\UFRMWalletKeys.pas' {FRMWalletKeys},
-  UGridUtils in 'gui-classic\UGridUtils.pas',
-  UGUIUtils in 'gui-classic\UGUIUtils.pas',
-  UPCDataTypes in 'core\UPCDataTypes.pas',
-  UPCOrderedLists in 'core\UPCOrderedLists.pas',
-  UPCOperationsSignatureValidator in 'core\UPCOperationsSignatureValidator.pas',
-  UPCTNetDataExtraMessages in 'core\UPCTNetDataExtraMessages.pas',
-  UFRMHashLock in 'gui-classic\UFRMHashLock.pas' {FRMHashLock},
-  URandomHash2 in 'core\URandomHash2.pas',
-  UFRMDiagnosticTool in 'gui-classic\UFRMDiagnosticTool.pas' {FRMDiagnosticTool},
-  UCommon in 'libraries\sphere10\UCommon.pas',
-  UMemory in 'libraries\sphere10\UMemory.pas',
-  UEPasa in 'core\UEPasa.pas',
-  UEncoding in 'core\UEncoding.pas';
+  UWallet in 'core\UWallet.pas';
 
 {$R *.res}
 

+ 50 - 31
src/pascalcoin_wallet_classic.dproj

@@ -125,31 +125,6 @@
         <DelphiCompile Include="$(MainSource)">
             <MainSource>MainSource</MainSource>
         </DelphiCompile>
-        <DCCReference Include="core\UAccountKeyStorage.pas"/>
-        <DCCReference Include="core\UAccounts.pas"/>
-        <DCCReference Include="core\UAES.pas"/>
-        <DCCReference Include="core\UBaseTypes.pas"/>
-        <DCCReference Include="core\UBlockChain.pas"/>
-        <DCCReference Include="core\UChunk.pas"/>
-        <DCCReference Include="core\UConst.pas"/>
-        <DCCReference Include="core\UCrypto.pas"/>
-        <DCCReference Include="core\UFileStorage.pas"/>
-        <DCCReference Include="core\ULog.pas"/>
-        <DCCReference Include="core\UNetProtection.pas"/>
-        <DCCReference Include="core\UNetProtocol.pas"/>
-        <DCCReference Include="core\UNode.pas"/>
-        <DCCReference Include="core\UOpTransaction.pas"/>
-        <DCCReference Include="core\UPoolMinerThreads.pas"/>
-        <DCCReference Include="core\UPoolMining.pas"/>
-        <DCCReference Include="core\URandomHash.pas"/>
-        <DCCReference Include="core\URPC.pas"/>
-        <DCCReference Include="core\USettings.pas"/>
-        <DCCReference Include="core\USha256.pas"/>
-        <DCCReference Include="core\UTCPIP.pas"/>
-        <DCCReference Include="core\UThread.pas"/>
-        <DCCReference Include="core\UTime.pas"/>
-        <DCCReference Include="core\UTxMultiOperation.pas"/>
-        <DCCReference Include="core\UWallet.pas"/>
         <DCCReference Include="gui-classic\UFRMAbout.pas">
             <Form>FRMAbout</Form>
             <FormType>dfm</FormType>
@@ -201,21 +176,65 @@
         </DCCReference>
         <DCCReference Include="gui-classic\UGridUtils.pas"/>
         <DCCReference Include="gui-classic\UGUIUtils.pas"/>
-        <DCCReference Include="core\UPCDataTypes.pas"/>
-        <DCCReference Include="core\UPCOrderedLists.pas"/>
-        <DCCReference Include="core\UPCOperationsSignatureValidator.pas"/>
-        <DCCReference Include="core\UPCTNetDataExtraMessages.pas"/>
         <DCCReference Include="gui-classic\UFRMHashLock.pas">
             <Form>FRMHashLock</Form>
         </DCCReference>
-        <DCCReference Include="core\URandomHash2.pas"/>
         <DCCReference Include="gui-classic\UFRMDiagnosticTool.pas">
             <Form>FRMDiagnosticTool</Form>
         </DCCReference>
         <DCCReference Include="libraries\sphere10\UCommon.pas"/>
         <DCCReference Include="libraries\sphere10\UMemory.pas"/>
-        <DCCReference Include="core\UEPasa.pas"/>
+        <DCCReference Include="core\UAccountKeyStorage.pas"/>
+        <DCCReference Include="core\UAccounts.pas"/>
+        <DCCReference Include="core\UAES.pas"/>
+        <DCCReference Include="core\UBaseTypes.pas"/>
+        <DCCReference Include="core\UBlockChain.pas"/>
+        <DCCReference Include="core\UChunk.pas"/>
+        <DCCReference Include="core\UConst.pas"/>
+        <DCCReference Include="core\UCrypto.pas"/>
+        <DCCReference Include="core\UECIES.pas"/>
         <DCCReference Include="core\UEncoding.pas"/>
+        <DCCReference Include="core\UEPasa.pas"/>
+        <DCCReference Include="core\UFileStorage.pas"/>
+        <DCCReference Include="core\ULog.pas"/>
+        <DCCReference Include="core\UMurMur3Fast.pas"/>
+        <DCCReference Include="core\UNetProtection.pas"/>
+        <DCCReference Include="core\UNetProtocol.pas"/>
+        <DCCReference Include="core\UNode.pas"/>
+        <DCCReference Include="core\UOpenSSL.pas"/>
+        <DCCReference Include="core\UOpTransaction.pas"/>
+        <DCCReference Include="core\UPCAbstractMem.pas"/>
+        <DCCReference Include="core\UPCAbstractMemAccountKeys.pas"/>
+        <DCCReference Include="core\UPCAbstractMemAccounts.pas"/>
+        <DCCReference Include="core\UPCAccountsOrdenations.pas"/>
+        <DCCReference Include="core\UPCCryptoLib4Pascal.pas"/>
+        <DCCReference Include="core\UPCDataTypes.pas"/>
+        <DCCReference Include="core\UPCEncryption.pas"/>
+        <DCCReference Include="core\UPCHardcodedRandomHashTable.pas"/>
+        <DCCReference Include="core\UPCOperationsBlockValidator.pas"/>
+        <DCCReference Include="core\UPCOperationsSignatureValidator.pas"/>
+        <DCCReference Include="core\UPCOrderedLists.pas"/>
+        <DCCReference Include="core\UPCRPCFileUtils.pas"/>
+        <DCCReference Include="core\UPCRPCFindAccounts.pas"/>
+        <DCCReference Include="core\UPCRPCFindBlocks.pas"/>
+        <DCCReference Include="core\UPCRPCOpData.pas"/>
+        <DCCReference Include="core\UPCRPCSend.pas"/>
+        <DCCReference Include="core\UPCSafeBoxRootHash.pas"/>
+        <DCCReference Include="core\UPCTemporalAbstractMem.pas"/>
+        <DCCReference Include="core\UPCTemporalFileStream.pas"/>
+        <DCCReference Include="core\UPCTNetDataExtraMessages.pas"/>
+        <DCCReference Include="core\UPoolMinerThreads.pas"/>
+        <DCCReference Include="core\UPoolMining.pas"/>
+        <DCCReference Include="core\URandomHash.pas"/>
+        <DCCReference Include="core\URandomHash2.pas"/>
+        <DCCReference Include="core\URPC.pas"/>
+        <DCCReference Include="core\USettings.pas"/>
+        <DCCReference Include="core\USha256.pas"/>
+        <DCCReference Include="core\UTCPIP.pas"/>
+        <DCCReference Include="core\UThread.pas"/>
+        <DCCReference Include="core\UTime.pas"/>
+        <DCCReference Include="core\UTxMultiOperation.pas"/>
+        <DCCReference Include="core\UWallet.pas"/>
         <BuildConfiguration Include="Debug">
             <Key>Cfg_2</Key>
             <CfgParent>Base</CfgParent>

+ 7 - 1
src/pascalcoin_wallet_classic.lpi

@@ -232,7 +232,7 @@
     </Target>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
-      <OtherUnitFiles Value="core;gui-classic;libraries\synapse;libraries\sphere10;libraries\cryptolib4pascal;libraries\simplebaselib4pascal;libraries\hashlib4pascal;libraries\generics.collections;libraries\pascalcoin;libraries\paszlib;libraries\gnugettext;libraries\abstractmem"/>
+      <OtherUnitFiles Value="core;gui-classic;libraries\synapse;libraries\sphere10;libraries\cryptolib4pascal;libraries\simplebaselib4pascal;libraries\hashlib4pascal;libraries\pascalcoin;libraries\paszlib;libraries\gnugettext;libraries\abstractmem;libraries\regex"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
     <Parsing>
@@ -240,8 +240,14 @@
         <SyntaxMode Value="Delphi"/>
       </SyntaxOptions>
     </Parsing>
+    <CodeGeneration>
+      <Optimizations>
+        <OptimizationLevel Value="2"/>
+      </Optimizations>
+    </CodeGeneration>
     <Linking>
       <Debugging>
+        <GenerateDebugInfo Value="False"/>
         <DebugInfoType Value="dsDwarf2Set"/>
       </Debugging>
       <Options>

Some files were not shown because too many files changed in this diff