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
 # 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
 - 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:
 - 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 "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_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 "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)
 - Improved performance when downloading Safebox (Fresh installation)
 - JSON-RPC changes:  
 - 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": 
   - 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"
   - 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)
     - "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
     - "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  
     - "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:
   - 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 "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
   - Fixed minor bugs
 
 
 ## Build 5.3.0 - 2020-03-12
 ## 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
 ## 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>
 		/// <summary>
 		/// Payload encryption and encoding method not specified.
 		/// Payload encryption and encoding method not specified.
 		/// </summary>
 		/// </summary>
-		NonDeterministic = 0x00000000,
+		NonDeterministic = 0b00000000,
 
 
 		/// <summary>
 		/// <summary>
 		/// Unencrypted, public payload.
 		/// Unencrypted, public payload.
 		/// </summary>
 		/// </summary>
-		Public = 0x00000001,
+		Public = 0b00000001,
 
 
 		/// <summary>
 		/// <summary>
 		/// ECIES encrypted using recipient accounts public key.
 		/// ECIES encrypted using recipient accounts public key.
 		/// </summary>		
 		/// </summary>		
-		RecipientKeyEncrypted = 0x00000010,
+		RecipientKeyEncrypted = 0b00000010,
 
 
 		/// <summary>
 		/// <summary>
 		/// ECIES encrypted using sender accounts public key.
 		/// ECIES encrypted using sender accounts public key.
 		/// </summary>
 		/// </summary>
-		SenderKeyEncrypted = 0x00000100,
+		SenderKeyEncrypted = 0b00000100,
 
 
 		/// <summary>
 		/// <summary>
 		/// AES encrypted using pwd param
 		/// AES encrypted using pwd param
 		/// </summary>
 		/// </summary>
-		PasswordEncrypted = 0x00001000,
+		PasswordEncrypted = 0b00001000,
 
 
 		/// <summary>
 		/// <summary>
 		/// Payload data encoded in ASCII
 		/// Payload data encoded in ASCII
 		/// </summary>
 		/// </summary>
-		AsciiFormatted = 0x00010000,
+		AsciiFormatted = 0b00010000,
 
 
 		/// <summary>
 		/// <summary>
 		/// Payload data encoded in HEX
 		/// Payload data encoded in HEX
 		/// </summary>
 		/// </summary>
-		HexFormatted = 0x00100000,
+		HexFormatted = 0b00100000,
 
 
 		/// <summary>
 		/// <summary>
 		/// Payload data encoded in Base58
 		/// Payload data encoded in Base58
 		/// </summary>
 		/// </summary>
-		Base58Formatted = 0x01000000,
+		Base58Formatted = 0b01000000,
 
 
 		/// <summary>
 		/// <summary>
 		/// E-PASA addressed by account name (not number).
 		/// E-PASA addressed by account name (not number).
 		/// </summary>
 		/// </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
 * Ugochukwu Mmaduekwe for assistance developing payload length validation rules
 * Benjamin Ansbach for regular feedback, assistance and insightful suggestions
 * Benjamin Ansbach for regular feedback, assistance and insightful suggestions
 * UrbanCohort for elegancy-improving suggestion
 * UrbanCohort for elegancy-improving suggestion
- 
+
 ## Reference Implementation
 ## Reference Implementation
 
 
 The following regex parses an e-pasa:
 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
 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
 [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
 [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]`    
 - `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.
   The data sequence of the operation. If the data sequence cannot be determined (null or not given), it is 0 by default.
 - `guid [GUID]`    
 - `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)` 
 - `amount [PASCCURRENCY] (defult = 0)` 
   The amount to transfer to `target`, if not given or null the default value is 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]`    
  - `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.    
     The data sequence of the operation. If the data sequence cannot be determined (null or not given), it is 0 by default.    
  - `guid [GUID]`    
  - `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_n_operation UINT32`
     Last value of `n_operation` of the signer (or sender or target)     
     Last value of `n_operation` of the signer (or sender or target)     
  - `amount [PASCCURRENCY] (defult = 0)` 
  - `amount [PASCCURRENCY] (defult = 0)` 
@@ -198,7 +200,8 @@ Array<Operation> finddataoperations({
 - `target [UINT32] (default = null)`    
 - `target [UINT32] (default = null)`    
   The account that received the DATA operation. Optional.
   The account that received the DATA operation. Optional.
 - `guid [GUID] (default = null)`    
 - `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]`    
 - `data_sequence [UINT16]`    
   The data sequence of the operation to search for. Optional.
   The data sequence of the operation to search for. Optional.
 - `data_type [UINT16]`    
 - `data_type [UINT16]`    
@@ -229,3 +232,5 @@ None.
 - UUID V4: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
 - 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
 - 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}
   {$DEFINE USE_ABSTRACTMEM}
 
 
   // Activate GNUGETTEXT library
   // Activate GNUGETTEXT library
-  {$DEFINE USE_GNUGETTEXT}
+  {.$DEFINE USE_GNUGETTEXT}
   
   
   // Activate usage of TPCTemporalFileStream instead of TBytes in order to minimize mem usage
   // Activate usage of TPCTemporalFileStream instead of TBytes in order to minimize mem usage
   // This also fixes issue #207 High memory usage on FreePascal compiler
   // 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
   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,
   UPCHardcodedRandomHashTable, UJSONFunctions,
   {$IFDEF USE_ABSTRACTMEM}
   {$IFDEF USE_ABSTRACTMEM}
   UPCAbstractMem, UPCAbstractMemAccountKeys,
   UPCAbstractMem, UPCAbstractMemAccountKeys,
+  {$ELSE}
   {$ENDIF}
   {$ENDIF}
+  UPCAccountsOrdenations,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 
 Type
 Type
@@ -216,6 +218,8 @@ Type
     FOrderedByName : TOrderedRawList;
     FOrderedByName : TOrderedRawList;
     // OrderedAccountKeysList (Added after Build 3.0.1) allows an indexed search of public keys in the safebox with mem optimization
     // OrderedAccountKeysList (Added after Build 3.0.1) allows an indexed search of public keys in the safebox with mem optimization
     FOrderedAccountKeysList : TSafeboxPubKeysAndAccounts;
     FOrderedAccountKeysList : TSafeboxPubKeysAndAccounts;
+    FAccountsOrderedByUpdatedBlock : TAccountsOrderedByUpdatedBlock;
+    FAccountsOrderedBySalePrice : TAccountsOrderedBySalePrice;
     {$ENDIF}
     {$ENDIF}
     FModifiedBlocksSeparatedChain : TOrderedBlockAccountList; // Used when has PreviousSafebox (Used if we are on a Separated chain)
     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);
     procedure SearchBlockWhenOnSeparatedChain(blockNumber : Cardinal; out blockAccount : TBlockAccount);
     function GetAggregatedHashrate: TBigNum;
     function GetAggregatedHashrate: TBigNum;
     function GetOrderedAccountKeysList: TSafeboxPubKeysAndAccounts;
     function GetOrderedAccountKeysList: TSafeboxPubKeysAndAccounts;
+    function GetAccount(AAccountNumber : Integer; var AAccount : TAccount) : Boolean;
   protected
   protected
     FTotalFee: Int64;
     FTotalFee: Int64;
     Procedure UpdateAccount(account_number : Cardinal; const newAccountInfo: TAccountInfo; const newName : TRawBytes; newType : Word;
     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 : String) : Integer; overload;
     Function FindAccountByName(const aName : TRawBytes) : Integer; overload;
     Function FindAccountByName(const aName : TRawBytes) : Integer; overload;
     Function FindAccountsStartingByName(const AStartName : TRawBytes; const ARawList : TOrderedRawList; const AMax : Integer = 0) : Integer;
     Function FindAccountsStartingByName(const AStartName : TRawBytes; const ARawList : TOrderedRawList; const AMax : Integer = 0) : Integer;
-
+    
     Procedure Clear;
     Procedure Clear;
     Function Account(account_number : Cardinal) : TAccount;
     Function Account(account_number : Cardinal) : TAccount;
     Function GetBlock(block_number : Cardinal) : TBlockAccount;
     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;
     class Function CopyAbstractMemToSafeBoxStream(ASource : TPCAbstractMem; ADestStream : TStream; AFromBlock, AToBlock : Cardinal; var AErrors : String) : Boolean;
     property PCAbstractMem : TPCAbstractMem read FPCAbstractMem;
     property PCAbstractMem : TPCAbstractMem read FPCAbstractMem;
     {$ENDIF}
     {$ENDIF}
+    Function AccountsOrderedByUpdatedBlock : TAccountsOrderedByUpdatedBlock;
+    Function AccountsOrderedBySalePrice : TAccountsOrderedBySalePrice;
   End;
   End;
 
 
 
 
@@ -494,7 +501,6 @@ uses
   // This issue is not detected on current Delphi memory manager (Tested on Delphi 10.3.2)
   // This issue is not detected on current Delphi memory manager (Tested on Delphi 10.3.2)
 {$ENDIF}
 {$ENDIF}
 
 
-
 { This function is for testing purpose only.
 { This function is for testing purpose only.
   Will check if Account Names are well assigned and stored }
   Will check if Account Names are well assigned and stored }
 function Check_Safebox_Names_Consistency(sb : TPCSafeBox; const title :String; var errors : String) : Boolean;
 function Check_Safebox_Names_Consistency(sb : TPCSafeBox; const title :String; var errors : String) : Boolean;
@@ -2408,6 +2414,24 @@ begin
   end;
   end;
 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;
 function TPCSafeBox.GetBlock(block_number: Cardinal): TBlockAccount;
 begin
 begin
   StartThreadSafe;
   StartThreadSafe;
@@ -2823,6 +2847,8 @@ begin
   FBlockAccountsList := TList<Pointer>.Create;
   FBlockAccountsList := TList<Pointer>.Create;
   FAggregatedHashrate := TBigNum.Create(0);
   FAggregatedHashrate := TBigNum.Create(0);
   FOrderedByName := TOrderedRawList.Create;
   FOrderedByName := TOrderedRawList.Create;
+  FAccountsOrderedByUpdatedBlock := TAccountsOrderedByUpdatedBlock.Create(GetAccount);
+  FAccountsOrderedBySalePrice := TAccountsOrderedBySalePrice.Create(GetAccount);
   {$ENDIF}
   {$ENDIF}
   FListOfOrderedAccountKeysList := TList<TOrderedAccountKeysList>.Create;
   FListOfOrderedAccountKeysList := TList<TOrderedAccountKeysList>.Create;
   FCurrentProtocol := CT_PROTOCOL_1;
   FCurrentProtocol := CT_PROTOCOL_1;
@@ -2866,6 +2892,11 @@ begin
   FreeAndNil(FAddedNamesSincePreviousSafebox);
   FreeAndNil(FAddedNamesSincePreviousSafebox);
   FreeAndNil(FDeletedNamesSincePreviousSafebox);
   FreeAndNil(FDeletedNamesSincePreviousSafebox);
   FreeAndNil(FSubChains);
   FreeAndNil(FSubChains);
+  {$IFnDEF USE_ABSTRACTMEM}
+  FreeAndNil(FAccountsOrderedByUpdatedBlock);
+  FreeAndNil(FAccountsOrderedBySalePrice);
+  {$ENDIF}
+
   If Assigned(FPreviousSafeBox) then begin
   If Assigned(FPreviousSafeBox) then begin
     FPreviousSafeBox.FSubChains.Remove(Self); // Remove from current snapshot
     FPreviousSafeBox.FSubChains.Remove(Self); // Remove from current snapshot
     FPreviousSafeBox := Nil;
     FPreviousSafeBox := Nil;
@@ -2968,7 +2999,7 @@ procedure TPCSafeBox.CommitToPrevious;
     // Start deleting:
     // Start deleting:
     For i:=0 to DeletedNamesList.Count-1 do begin
     For i:=0 to DeletedNamesList.Count-1 do begin
       {$IFDEF USE_ABSTRACTMEM}
       {$IFDEF USE_ABSTRACTMEM}
-      FPreviousSafebox.FPCAbstractMem.AccountsNames.Remove(DeletedNamesList.Get(i).ToString);
+      FPreviousSafebox.FPCAbstractMem.AccountsNames.DeleteAccountName(DeletedNamesList.Get(i).ToString);
       {$ELSE}
       {$ELSE}
       FPreviousSafebox.FOrderedByName.Remove(DeletedNamesList.Get(i));
       FPreviousSafebox.FOrderedByName.Remove(DeletedNamesList.Get(i));
       {$ENDIF}
       {$ENDIF}
@@ -2976,7 +3007,7 @@ procedure TPCSafeBox.CommitToPrevious;
     // Finally adding
     // Finally adding
     For i:=0 to AddedNamesList.Count-1 do begin
     For i:=0 to AddedNamesList.Count-1 do begin
       {$IFDEF USE_ABSTRACTMEM}
       {$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}
       {$ELSE}
       FPreviousSafebox.FOrderedByName.Add(AddedNamesList.Get(i),AddedNamesList.GetTag(i));
       FPreviousSafebox.FOrderedByName.Add(AddedNamesList.Get(i),AddedNamesList.GetTag(i));
       {$ENDIF}
       {$ENDIF}
@@ -3111,19 +3142,23 @@ procedure TPCSafeBox.RollBackToSnapshot(snapshotBlock: Cardinal);
 
 
    Procedure UndoAddedDeletedNames(AddedNamesList,DeletedNamesList : TOrderedRawList);
    Procedure UndoAddedDeletedNames(AddedNamesList,DeletedNamesList : TOrderedRawList);
    Var i,j : Integer;
    Var i,j : Integer;
+     {$IFDEF USE_ABSTRACTMEM}
+     Laninfo : TAccountNameInfo;
+     {$ELSE}
+     {$ENDIF}
    Begin
    Begin
      // Start adding
      // Start adding
      For i:=0 to AddedNamesList.Count-1 do begin
      For i:=0 to AddedNamesList.Count-1 do begin
        // It was added, so we MUST FIND on current names list
        // It was added, so we MUST FIND on current names list
        {$IFDEF USE_ABSTRACTMEM}
        {$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???
          // ERROR: It has been added, why we not found???
          If DeletedNamesList.Find(AddedNamesList.Get(i),j) then begin
          If DeletedNamesList.Find(AddedNamesList.Get(i),j) then begin
          end else 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)]));
            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;
        end else begin
        end else begin
-         FPCAbstractMem.AccountsNames.Delete(j);
+         FPCAbstractMem.AccountsNames.DeleteData(Laninfo);
        end;
        end;
        {$ELSE}
        {$ELSE}
        If Not FOrderedByName.Find(AddedNamesList.Get(i),j) then begin
        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
      For i:=0 to DeletedNamesList.Count-1 do begin
        {$IFDEF USE_ABSTRACTMEM}
        {$IFDEF USE_ABSTRACTMEM}
        // It has been deleted, we MUST NOT FIND on current names list
        // 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???
            // 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;
        end;
        end;
        // Add with Info of previous account with name (saved at Tag value)
        // 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}
        {$ELSE}
        // It has been deleted, we MUST NOT FIND on current names list
        // It has been deleted, we MUST NOT FIND on current names list
        If FOrderedByName.Find(DeletedNamesList.Get(i),j) then begin
        If FOrderedByName.Find(DeletedNamesList.Get(i),j) then begin
@@ -3412,7 +3446,7 @@ begin
       if sbHeader.blocksCount<FPCAbstractMem.BlocksCount then begin
       if sbHeader.blocksCount<FPCAbstractMem.BlocksCount then begin
         FPCAbstractMem.EraseData;
         FPCAbstractMem.EraseData;
       end else begin
       end else begin
-        FPCAbstractMem.AccountsNames.Clear;
+        FPCAbstractMem.AccountsNames.EraseTree;
       end;
       end;
       AggregatedHashrate.Value := 0;
       AggregatedHashrate.Value := 0;
       {$ELSE}
       {$ELSE}
@@ -3514,11 +3548,11 @@ begin
               Exit;
               Exit;
             end;
             end;
             {$IFDEF USE_ABSTRACTMEM}
             {$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+'"';
               errors := errors + ' Duplicate name "'+LBlock.accounts[iacc].name.ToPrintable+'"';
               Exit;
               Exit;
             end;
             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}
             {$ELSE}
             if FOrderedByName.IndexOf(LBlock.accounts[iacc].name)>=0 then begin
             if FOrderedByName.IndexOf(LBlock.accounts[iacc].name)>=0 then begin
               errors := errors + ' Duplicate name "'+LBlock.accounts[iacc].name.ToPrintable+'"';
               errors := errors + ' Duplicate name "'+LBlock.accounts[iacc].name.ToPrintable+'"';
@@ -3555,9 +3589,9 @@ begin
             if ((iblock + (CT_BankToDiskEveryNBlocks * 10)) >= sbHeader.blockscount) then begin
             if ((iblock + (CT_BankToDiskEveryNBlocks * 10)) >= sbHeader.blockscount) then begin
             {$ENDIF}
             {$ENDIF}
               {$IFDEF ASSUME_VALID_POW_OLD_PROTOCOLS}
               {$IFDEF ASSUME_VALID_POW_OLD_PROTOCOLS}
-              LAddToMultiThreadOperationsBlockValidator := (LUseMultiThreadOperationsBlockValidator) and (LBlock.blockchainInfo.protocol_version>=CT_PROTOCOL_5) and (Assigned(LPCOperationsBlockValidator));
+              LAddToMultiThreadOperationsBlockValidator := False;
               {$ELSE}
               {$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}
               {$ENDIF}
               If not IsValidNewOperationsBlock(LBlock.blockchainInfo,False,Not LAddToMultiThreadOperationsBlockValidator,aux_errors) then begin
               If not IsValidNewOperationsBlock(LBlock.blockchainInfo,False,Not LAddToMultiThreadOperationsBlockValidator,aux_errors) then begin
                 errors := errors + ' > ' + aux_errors;
                 errors := errors + ' > ' + aux_errors;
@@ -3603,6 +3637,7 @@ begin
             Exit;
             Exit;
           end;
           end;
         end;
         end;
+
         // Add
         // Add
         {$IFDEF USE_ABSTRACTMEM}
         {$IFDEF USE_ABSTRACTMEM}
         FPCAbstractMem.SetBlockAccount(LBlock);
         FPCAbstractMem.SetBlockAccount(LBlock);
@@ -3613,6 +3648,17 @@ begin
         // BufferBlocksHash fill with data
         // BufferBlocksHash fill with data
         j := (length(LBlock.block_hash)*(iBlock));
         j := (length(LBlock.block_hash)*(iBlock));
         BufferBlocksHash.Replace( j, LBlock.block_hash[0], 32 );
         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}
         {$ENDIF}
         for j := low(LBlock.accounts) to High(LBlock.accounts) do begin
         for j := low(LBlock.accounts) to High(LBlock.accounts) do begin
           AccountKeyListAddAccounts(LBlock.accounts[j].accountInfo.accountKey,[LBlock.accounts[j].account]);
           AccountKeyListAddAccounts(LBlock.accounts[j].accountInfo.accountKey,[LBlock.accounts[j].account]);
@@ -3636,7 +3682,6 @@ begin
         // Assign to previous
         // Assign to previous
         LPreviousProofOfWork := LBlock.blockchainInfo.proof_of_work;
         LPreviousProofOfWork := LBlock.blockchainInfo.proof_of_work;
       end; // For iBlock ...
       end; // For iBlock ...
-
         if Assigned(LPCOperationsBlockValidator) then begin
         if Assigned(LPCOperationsBlockValidator) then begin
           repeat
           repeat
             LPCOperationsBlockValidator.GetStatus(LValidatedOPOk, LValidatedOPError, LValidatedOPPending);
             LPCOperationsBlockValidator.GetStatus(LValidatedOPOk, LValidatedOPError, LValidatedOPPending);
@@ -4536,6 +4581,12 @@ begin
   end;
   end;
 end;
 end;
 
 
+function TPCSafeBox.GetAccount(AAccountNumber: Integer; var AAccount: TAccount): Boolean;
+begin
+  AAccount := Account(AAccountNumber);
+  Result := True;
+end;
+
 function TPCSafeBox.GetActualCompactTargetHash(protocolVersion : Word): Cardinal;
 function TPCSafeBox.GetActualCompactTargetHash(protocolVersion : Word): Cardinal;
 begin
 begin
   Result := TPascalCoinProtocol.TargetToCompact(GetActualTargetHash(protocolVersion),protocolVersion);
   Result := TPascalCoinProtocol.TargetToCompact(GetActualTargetHash(protocolVersion),protocolVersion);
@@ -4549,10 +4600,13 @@ end;
 function TPCSafeBox.FindAccountByName(const aName: TRawBytes): Integer;
 function TPCSafeBox.FindAccountByName(const aName: TRawBytes): Integer;
 Var i,j,k : Integer;
 Var i,j,k : Integer;
   Psnapshot : PSafeboxSnapshot;
   Psnapshot : PSafeboxSnapshot;
+  {$IFDEF USE_ABSTRACTMEM}
+  Laninfo : TAccountNameInfo;
+  {$ENDIF}
 begin
 begin
   {$IFDEF USE_ABSTRACTMEM}
   {$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}
   {$ELSE}
   i := FOrderedByName.IndexOf(aName);
   i := FOrderedByName.IndexOf(aName);
   if i>=0 then Result := FOrderedByName.GetTag(i)
   if i>=0 then Result := FOrderedByName.GetTag(i)
@@ -4607,26 +4661,24 @@ end;
 
 
 function TPCSafeBox.FindAccountsStartingByName(const AStartName: TRawBytes;
 function TPCSafeBox.FindAccountsStartingByName(const AStartName: TRawBytes;
   const ARawList: TOrderedRawList; const AMax: Integer = 0): Integer;
   const ARawList: TOrderedRawList; const AMax: Integer = 0): Integer;
-var LIndex : Integer;
+var
   LRaw : TRawBytes;
   LRaw : TRawBytes;
-  LStartNameString : String;
+  {$IFDEF USE_ABSTRACTMEM}
+  Laninfo : TAccountNameInfo;
+  {$ELSE}
+  LIndex : Integer;
+  {$ENDIF}
 begin
 begin
   ARawList.Clear;
   ARawList.Clear;
   StartThreadSafe;
   StartThreadSafe;
   try
   try
     {$IFDEF USE_ABSTRACTMEM}
     {$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;
     end;
     {$ELSE}
     {$ELSE}
     if FOrderedByName.Find(AStartName,LIndex) then begin
     if FOrderedByName.Find(AStartName,LIndex) then begin
@@ -4703,6 +4755,8 @@ Var iBlock : Cardinal;
   blockAccount : TBlockAccount;
   blockAccount : TBlockAccount;
   {$IFnDEF USE_ABSTRACTMEM}
   {$IFnDEF USE_ABSTRACTMEM}
   Pblock : PBlockAccount;
   Pblock : PBlockAccount;
+  {$ELSE}
+  Laninfo : TAccountNameInfo;
   {$ENDIF}
   {$ENDIF}
 begin
 begin
   iBlock := account_number DIV CT_AccountsPerBlock;
   iBlock := account_number DIV CT_AccountsPerBlock;
@@ -4716,6 +4770,16 @@ begin
   end else begin
   end else begin
     Pblock := FBlockAccountsList.Items[iBlock];
     Pblock := FBlockAccountsList.Items[iBlock];
   end;
   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}
   {$ENDIF}
 
 
   if (NOT TAccountComp.EqualAccountKeys(blockAccount.accounts[iAccount].accountInfo.accountKey,newAccountInfo.accountKey)) then begin
   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
       If Length(blockAccount.accounts[iAccount].name)>0 then begin
 
 
         {$IFDEF USE_ABSTRACTMEM}
         {$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
           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));
             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;
         end else begin
         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;
           end;
-          FPCAbstractMem.AccountsNames.Delete(i);
+          FPCAbstractMem.AccountsNames.DeleteData(Laninfo);
         end;
         end;
         {$ELSE}
         {$ELSE}
         i := FOrderedByName.IndexOf(blockAccount.accounts[iAccount].name);
         i := FOrderedByName.IndexOf(blockAccount.accounts[iAccount].name);
@@ -4793,9 +4856,11 @@ begin
       blockAccount.accounts[iAccount].name:=newName;
       blockAccount.accounts[iAccount].name:=newName;
       If Length(blockAccount.accounts[iAccount].name)>0 then begin
       If Length(blockAccount.accounts[iAccount].name)>0 then begin
         {$IFDEF USE_ABSTRACTMEM}
         {$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}
         {$ELSE}
         i := FOrderedByName.IndexOf(blockAccount.accounts[iAccount].name);
         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)));
         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;
     Balance : Int64;
     OriginalPayload : TOperationPayload;
     OriginalPayload : TOperationPayload;
     PrintablePayload : String;
     PrintablePayload : String;
+    DecodedEPasaPayload : String;
     OperationHash : TRawBytes;
     OperationHash : TRawBytes;
     OperationHash_OLD : TRawBytes; // Will include old oeration hash value
     OperationHash_OLD : TRawBytes; // Will include old oeration hash value
     errors : String;
     errors : String;
@@ -574,7 +575,7 @@ Type
 
 
 Const
 Const
   CT_TOperationPayload_NUL : TOperationPayload = (payload_type:0;payload_raw:Nil);
   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_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_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;
   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;
   l := FList.LockList;
   try
   try
     if index<l.Count then Result := POperationResume(l[index])^
     if index<l.Count then Result := POperationResume(l[index])^
-    else Result := CT_TOperationResume_NUL;
+    else begin
+      Result := CT_TOperationResume_NUL;
+    end;
   finally
   finally
     FList.UnlockList;
     FList.UnlockList;
   end;
   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_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!!!
   // IMPORTANT NOTE!!!
   // NetProtocol_Available MUST BE always >= NetProtocol_version
   // NetProtocol_Available MUST BE always >= NetProtocol_version
   CT_NetProtocol_Available: Word = {$IFDEF PRODUCTION}12{$ELSE}12{$ENDIF};
   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_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;
   CT_PseudoOp_Reward = $0;
   // Value of Operations type in Protocol 1
   // Value of Operations type in Protocol 1
@@ -198,7 +198,7 @@ Const
   CT_OpSubtype_Data_Signer                = 103;
   CT_OpSubtype_Data_Signer                = 103;
   CT_OpSubtype_Data_Receiver              = 104;
   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'
   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};
                     {$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  = 1;
   CT_MOLINA_DECIMAL = {$IFDEF FPC}Real(CT_MOLINA/1000.0);{$ELSE}0.0001;{$ENDIF}
   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};
   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
   // App Params
   CT_PARAM_GridAccountsStream = 'GridAccountsStreamV2';
   CT_PARAM_GridAccountsStream = 'GridAccountsStreamV2';
   CT_PARAM_GridAccountsPos = 'GridAccountsPos';
   CT_PARAM_GridAccountsPos = 'GridAccountsPos';

+ 292 - 117
src/core/UEPasa.pas

@@ -27,10 +27,10 @@ uses
   SysUtils,
   SysUtils,
   TypInfo,
   TypInfo,
   uregexpr,
   uregexpr,
-  UAccounts,
   UCommon,
   UCommon,
   UCrypto,
   UCrypto,
-  UEncoding;
+  UEncoding,
+  SyncObjs;
 
 
 type
 type
 
 
@@ -43,89 +43,106 @@ type
     BadExtendedChecksum
     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
   public
-    function Value: Int32;
+    function ToProtocolValue: byte;
   end;
   end;
 
 
-  { PayloadTypes }
+  { TPayloadType }
 
 
-  PayloadTypes = set of PayloadType;
+  TPayloadType = set of TPayloadTrait;
 
 
-  { PayloadTypesHelper }
+  { TPayloadTypesHelper }
 
 
-  PayloadTypesHelper = record helper for PayloadTypes
+  TPayloadTypeHelper = record helper for TPayloadType
   public
   public
-    function IsPayloadTypeInSet(APayloadType: PayloadType): Boolean; inline;
+    function HasTrait(APayloadTrait: TPayloadTrait): Boolean; inline;
+    function ToProtocolValue : byte;
+    function IsValid : Boolean;
   end;
   end;
 
 
-
   { TEPasa }
   { TEPasa }
 
 
+
   TEPasa = record
   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;
       function GetAccount: TNullable<UInt32>; inline;
       procedure SetAccount(const AValue: TNullable<UInt32>); inline;
       procedure SetAccount(const AValue: TNullable<UInt32>); inline;
       function GetAccountChecksum: TNullable<UInt32>; inline;
       function GetAccountChecksum: TNullable<UInt32>; inline;
       procedure SetAccountChecksum(const AValue: TNullable<UInt32>); inline;
       procedure SetAccountChecksum(const AValue: TNullable<UInt32>); inline;
-      function GetPayloadTypes: PayloadTypes; inline;
+      function GetPayloadType: TPayloadType; inline;
       function GetAccountName: String; inline;
       function GetAccountName: String; inline;
       procedure SetAccountName(const AValue: String); inline;
       procedure SetAccountName(const AValue: String); inline;
-      procedure SetPayloadTypes(const AValue: PayloadTypes); inline;
+      procedure SetPayloadType(const AValue: TPayloadType); inline;
       function GetExtendedChecksum: String; inline;
       function GetExtendedChecksum: String; inline;
       procedure SetExtendedChecksum(const AValue: String); inline;
       procedure SetExtendedChecksum(const AValue: String); inline;
       function GetPassword: String; inline;
       function GetPassword: String; inline;
       procedure SetPassword(const AValue: String); inline;
       procedure SetPassword(const AValue: String); inline;
       function GetPayload: String; inline;
       function GetPayload: String; inline;
       procedure SetPayload(const AValue: 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
     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 Account: TNullable<UInt32> read GetAccount write SetAccount;
       property AccountChecksum: TNullable<UInt32> read GetAccountChecksum write SetAccountChecksum;
       property AccountChecksum: TNullable<UInt32> read GetAccountChecksum write SetAccountChecksum;
       property AccountName: String read GetAccountName write SetAccountName;
       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 Payload: String read GetPayload write SetPayload;
       property Password: String read GetPassword write SetPassword;
       property Password: String read GetPassword write SetPassword;
       property ExtendedChecksum: String read GetExtendedChecksum write SetExtendedChecksum;
       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 Parse(const AEPasaText: String): TEPasa; static;
 
 
       class function CalculateAccountChecksum(AAccNo: UInt32): Byte; static; inline;
       class function CalculateAccountChecksum(AAccNo: UInt32): Byte; static; inline;
-
+      procedure Clear;
   end;
   end;
 
 
+
+
   { TEPasaParser }
   { TEPasaParser }
 
 
   TEPasaParser = class
   TEPasaParser = class
     strict private
     strict private
       class var FEPasaRegex: TCustomRegex;
       class var FEPasaRegex: TCustomRegex;
+      class var FEPasaLocker : TCriticalSection;
       class constructor CreateRegexEPasaParser();
       class constructor CreateRegexEPasaParser();
       class destructor DestroyRegexEPasaParser();
       class destructor DestroyRegexEPasaParser();
 
 
@@ -134,7 +151,7 @@ type
       // note: regex syntax escapes following chars [\^$.|?*+(){}
       // note: regex syntax escapes following chars [\^$.|?*+(){}
       // note: epasa syntax escapes following chars: :\"[]()<>(){}
       // note: epasa syntax escapes following chars: :\"[]()<>(){}
       // note: c-sharp syntax verbatim strings escape: " as ""
       // note: c-sharp syntax verbatim strings escape: " as ""
-      IntegerPattern = '(0|[1-9]\d+)';
+      IntegerPattern = '(0|[1-9]\d*)';
       AccountNamePattern = '(?P<AccountName>' + TPascal64Encoding.StringPattern + ')';
       AccountNamePattern = '(?P<AccountName>' + TPascal64Encoding.StringPattern + ')';
       AccountChecksumPattern = '(?:(?P<ChecksumDelim>-)(?P<Checksum>\d{2}))?';
       AccountChecksumPattern = '(?:(?P<ChecksumDelim>-)(?P<Checksum>\d{2}))?';
       AccountNumberPattern = '(?P<AccountNumber>' + IntegerPattern + ')' + AccountChecksumPattern;
       AccountNumberPattern = '(?P<AccountNumber>' + IntegerPattern + ')' + AccountChecksumPattern;
@@ -153,11 +170,12 @@ type
       function Parse(const AEPasaText: String): TEPasa;
       function Parse(const AEPasaText: String): TEPasa;
       function TryParse(const AEPasaText: String; out AEPasa: TEPasa): Boolean; overload;
       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; out AEPasa: TEPasa; out AErrorCode: EPasaErrorCode): Boolean; overload;
+      function TryParse(const AEPasaText: String; AOmitExtendedChecksumVerification : Boolean; out AEPasa: TEPasa; out AErrorCode: EPasaErrorCode): Boolean; overload;
   end;
   end;
 
 
-  { TEPasaHelper }
+  { TEPasaComp }
 
 
-  TEPasaHelper = class sealed(TObject)
+  TEPasaComp = class sealed(TObject)
 
 
     strict private
     strict private
       class function ReadUInt16AsBytesLE(AValue: UInt16): TArray<Byte>; static;
       class function ReadUInt16AsBytesLE(AValue: UInt16): TArray<Byte>; static;
@@ -177,9 +195,12 @@ type
 
 
       class function ComputeExtendedChecksum(const AText: String): String; static;
       class function ComputeExtendedChecksum(const AText: String): String; static;
       class function IsValidExtendedChecksum(const AText: String; const AChecksum: String): Boolean; 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 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;
   end;
 
 
 resourcestring
 resourcestring
@@ -200,32 +221,79 @@ uses
   HlpConverters,
   HlpConverters,
   UMemory;
   UMemory;
 
 
-{ PayloadTypeHelper }
+var
+  EmptyEPasa : TEPasa;
+
+{ TPayloadTraitHelper }
 
 
-function PayloadTypeHelper.Value: Int32;
+function TPayloadTraitHelper.ToProtocolValue: Byte;
 begin
 begin
   case Self of
   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;
   end;
+  raise Exception.Create('Internal Error 2faed11a-1b0f-447a-87d1-2e1735ac4ca2');
 end;
 end;
 
 
-{ PayloadTypesHelper }
+{ TPayloadTypeHelper }
 
 
-function PayloadTypesHelper.IsPayloadTypeInSet(APayloadType : PayloadType) : Boolean;
+function TPayloadTypeHelper.HasTrait(APayloadTrait : TPayloadTrait) : Boolean;
 begin
 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;
 end;
 
 
 { TEPasa }
 { 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>;
 function TEPasa.GetAccount: TNullable<UInt32>;
 begin
 begin
   Result := FAccount;
   Result := FAccount;
@@ -256,9 +324,9 @@ begin
   Result := FPayload;
   Result := FPayload;
 end;
 end;
 
 
-function TEPasa.GetPayloadTypes: PayloadTypes;
+function TEPasa.GetPayloadType: TPayloadType;
 begin
 begin
-  Result := FPayloadTypes;
+  Result := FPayloadType;
 end;
 end;
 
 
 procedure TEPasa.SetAccount(const AValue: TNullable<UInt32>);
 procedure TEPasa.SetAccount(const AValue: TNullable<UInt32>);
@@ -291,89 +359,128 @@ begin
   FPayload := AValue;
   FPayload := AValue;
 end;
 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
 begin
-  FPayloadTypes := AValue;
+  Result := (NOT IsPayToKey) AND PayloadType.HasTrait(ptAddressedByName);
 end;
 end;
 
 
-function TEPasa.IsPayToKey: Boolean;
+function TEPasa.GetIsPayToKey: Boolean;
 begin
 begin
   Result :=
   Result :=
     (AccountName = '@') and
     (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;
 end;
 
 
-function TEPasa.GetRawPayloadBytes: TArray<Byte>;
+function TEPasa.GetHasPayload: Boolean;
 begin
 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));
     Exit(TEncoding.ASCII.GetBytes(Payload));
 
 
-  if (PayloadTypes.IsPayloadTypeInSet(PayloadType.Base58Formatted)) then
+  if (PayloadType.HasTrait(ptBase58Formatted)) then
     Exit(TPascalBase58Encoding.Decode(Payload));
     Exit(TPascalBase58Encoding.Decode(Payload));
 
 
-  if (PayloadTypes.IsPayloadTypeInSet(PayloadType.HexFormatted)) then
+  if (PayloadType.HasTrait(ptHexFormatted)) then
     Exit(THexEncoding.Decode(Payload));
     Exit(THexEncoding.Decode(Payload));
 
 
   raise EPascalCoinException.CreateRes(@SUnknownPayloadEncoding);
   raise EPascalCoinException.CreateRes(@SUnknownPayloadEncoding);
 end;
 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;
 function TEPasa.ToString(AOmitExtendedChecksum: Boolean): String;
 var
 var
   LPayloadContent: String;
   LPayloadContent: String;
 begin
 begin
   Result := string.Empty;
   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);
     Result := Result + TPascal64Encoding.Escape(AccountName);
   end else begin
   end else begin
+    if (Not Account.HasValue) then Exit;
     Result := Result + Account.Value.ToString();
     Result := Result + Account.Value.ToString();
     if (AccountChecksum.HasValue) then begin
     if (AccountChecksum.HasValue) then begin
       Result := Result + String.Format('-%u', [AccountChecksum.Value]);
       Result := Result + String.Format('-%u', [AccountChecksum.Value]);
     end;
     end;
   end;
   end;
 
 
-  if (PayloadTypes.IsPayloadTypeInSet(PayloadType.AsciiFormatted)) then begin
+  if (PayloadType.HasTrait(ptAsciiFormatted)) then begin
     LPayloadContent := String.Format('"%s"', [TPascalAsciiEncoding.Escape(Payload)]);
     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]);
     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]);
     LPayloadContent := string.Format('%s', [Payload]);
   end else begin
   end else begin
     // it is non-deterministic, so payload content is ignored
     // it is non-deterministic, so payload content is ignored
     LPayloadContent := string.Empty;
     LPayloadContent := string.Empty;
   end;
   end;
 
 
-  if (PayloadTypes.IsPayloadTypeInSet(PayloadType.Public)) then begin
+  if (PayloadType.HasTrait(ptPublic)) then begin
     Result := Result + string.Format('[%s]', [LPayloadContent]);
     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]);
     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]);
     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)]);
     Result := Result + string.Format('{%s:%s}', [LPayloadContent, TPascalAsciiEncoding.Escape(Password)]);
   end else begin
   end else begin
     // it is non-deterministic, so payload omitted entirely
     // it is non-deterministic, so payload omitted entirely
   end;
   end;
 
 
   if (not AOmitExtendedChecksum) then begin
   if (not AOmitExtendedChecksum) then begin
+    if (ExtendedChecksum='') then begin
+      // Need to compute:
+      ExtendedChecksum := TEPasaComp.ComputeExtendedChecksum(Result);
+    end;
     Result := Result + string.Format(':%s', [ExtendedChecksum]);
     Result := Result + string.Format(':%s', [ExtendedChecksum]);
   end;
   end;
 end;
 end;
 
 
-function TEPasa.ToString: String;
+
+
+class function TEPasa.TryParse(const AEPasaText: String; out AEPasa: TEPasa): Boolean;
 begin
 begin
-  Result := ToString(False);
+  Result := TryParse(AEPasaText,False,AEPasa);
 end;
 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
 var
   LParser: TEPasaParser;
   LParser: TEPasaParser;
   LDisposables : TDisposables;
   LDisposables : TDisposables;
-
+  LEPasaErrorCode : EPasaErrorCode;
 begin
 begin
   LParser := LDisposables.AddObject( TEPasaParser.Create() ) as TEPasaParser;
   LParser := LDisposables.AddObject( TEPasaParser.Create() ) as TEPasaParser;
-  Result := LParser.TryParse(AEPasaText, AEPasa);
+  Result := LParser.TryParse(AEPasaText,AOmitExtendedChecksumVerification,AEPasa,LEPasaErrorCode);
 end;
 end;
 
 
 class function TEPasa.Parse(const AEPasaText: String): TEPasa;
 class function TEPasa.Parse(const AEPasaText: String): TEPasa;
@@ -389,16 +496,23 @@ begin
 end;
 end;
 
 
 
 
+class function TEPasa.GetEmptyValue : TEPasa;
+begin
+  Result := EmptyEPasa;
+end;
+
 { TEPasaParser }
 { TEPasaParser }
 
 
 class constructor TEPasaParser.CreateRegexEPasaParser;
 class constructor TEPasaParser.CreateRegexEPasaParser;
 begin
 begin
   FEPasaRegex := TCustomRegex.Create(EPasaPattern);
   FEPasaRegex := TCustomRegex.Create(EPasaPattern);
+  FEPasaLocker := TCriticalSection.Create;
 end;
 end;
 
 
 class destructor TEPasaParser.DestroyRegexEPasaParser;
 class destructor TEPasaParser.DestroyRegexEPasaParser;
 begin
 begin
   FEPasaRegex.Free;
   FEPasaRegex.Free;
+  FEPasaLocker.Free;
 end;
 end;
 
 
 function TEPasaParser.Parse(const AEPasaText: String): TEPasa;
 function TEPasaParser.Parse(const AEPasaText: String): TEPasa;
@@ -419,21 +533,28 @@ begin
 end;
 end;
 
 
 function TEPasaParser.TryParse(const AEPasaText: String; out AEPasa: TEPasa; out AErrorCode: EPasaErrorCode): Boolean;
 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
 var
   LChecksumDelim, LAccountNumber, LAccountChecksum, LAccountName, LPayloadStartChar,
   LChecksumDelim, LAccountNumber, LAccountChecksum, LAccountName, LPayloadStartChar,
   LPayloadEndChar, LPayloadContent, LPayloadPasswordDelim, LPayloadPassword,
   LPayloadEndChar, LPayloadContent, LPayloadPasswordDelim, LPayloadPassword,
   LExtendedChecksumDelim, LExtendedChecksum, LActualChecksum: String;
   LExtendedChecksumDelim, LExtendedChecksum, LActualChecksum: String;
   LAccNo, LAccChecksum: UInt32;
   LAccNo, LAccChecksum: UInt32;
   LActualAccountChecksum: Byte;
   LActualAccountChecksum: Byte;
-  LEPasa : TEPasa;
 begin
 begin
   AErrorCode := EPasaErrorCode.Success;
   AErrorCode := EPasaErrorCode.Success;
-  AEPasa := LEPasa;
+  AEPasa.Clear;
   if (string.IsNullOrEmpty(AEPasaText)) then begin
   if (string.IsNullOrEmpty(AEPasaText)) then begin
     AErrorCode := EPasaErrorCode.BadFormat;
     AErrorCode := EPasaErrorCode.BadFormat;
     Exit(False);
     Exit(False);
   end;
   end;
 
 
+  FEPasaLocker.Acquire; // Protect against multithread
+  Try
+
   FEPasaRegex.Match(AEPasaText);
   FEPasaRegex.Match(AEPasaText);
 
 
   LChecksumDelim := FEPasaRegex.GetMatchFromName('ChecksumDelim');
   LChecksumDelim := FEPasaRegex.GetMatchFromName('ChecksumDelim');
@@ -454,6 +575,10 @@ begin
     Exit(False);
     Exit(False);
   end;
   end;
 
 
+  Finally
+    FEPasaLocker.Release;
+  End;
+
   if (LAccountName <> #0) then begin
   if (LAccountName <> #0) then begin
     // Account Name
     // Account Name
     if (string.IsNullOrEmpty(LAccountName)) then begin
     if (string.IsNullOrEmpty(LAccountName)) then begin
@@ -465,8 +590,7 @@ begin
     // when multiple enums are OR'ed in C#, they are combined and
     // when multiple enums are OR'ed in C#, they are combined and
     // if any of the enums numeric value is zero, it is excluded.
     // 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.
     // 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.AccountName := TPascal64Encoding.Unescape(LAccountName);
     AEPasa.Account := Nil;
     AEPasa.Account := Nil;
     AEPasa.AccountChecksum := Nil;
     AEPasa.AccountChecksum := Nil;
@@ -504,23 +628,23 @@ begin
           AErrorCode := EPasaErrorCode.MismatchedPayloadEncoding;
           AErrorCode := EPasaErrorCode.MismatchedPayloadEncoding;
           Exit(False);
           Exit(False);
         end;
         end;
-        AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.Public] -
-          [PayloadType.NonDeterministic];
+        AEPasa.PayloadType := AEPasa.PayloadType + [ptPublic] -
+          [ptNonDeterministic];
       end;
       end;
      '(': begin
      '(': begin
         if (LPayloadEndChar <> ')') then begin
         if (LPayloadEndChar <> ')') then begin
           AErrorCode := EPasaErrorCode.MismatchedPayloadEncoding;
           AErrorCode := EPasaErrorCode.MismatchedPayloadEncoding;
           Exit(False);
           Exit(False);
         end;
         end;
-        AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.RecipientKeyEncrypted] - [PayloadType.NonDeterministic];
+        AEPasa.PayloadType := AEPasa.PayloadType + [ptRecipientKeyEncrypted] - [ptNonDeterministic];
       end;
       end;
     '<': begin
     '<': begin
         if (LPayloadEndChar <> '>') then begin
         if (LPayloadEndChar <> '>') then begin
           AErrorCode := EPasaErrorCode.MismatchedPayloadEncoding;
           AErrorCode := EPasaErrorCode.MismatchedPayloadEncoding;
           Exit(False);
           Exit(False);
         end;
         end;
-        AEPasa.PayloadTypes := AEPasa.PayloadTypes +
-          [PayloadType.SenderKeyEncrypted] - [PayloadType.NonDeterministic];
+        AEPasa.PayloadType := AEPasa.PayloadType +
+          [ptSenderKeyEncrypted] - [ptNonDeterministic];
       end;
       end;
 
 
     '{': begin
     '{': begin
@@ -528,13 +652,13 @@ begin
           AErrorCode := EPasaErrorCode.MismatchedPayloadEncoding;
           AErrorCode := EPasaErrorCode.MismatchedPayloadEncoding;
           Exit(False);
           Exit(False);
         end;
         end;
-        AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.PasswordEncrypted] - [PayloadType.NonDeterministic];
+        AEPasa.PayloadType := AEPasa.PayloadType + [ptPasswordEncrypted] - [ptNonDeterministic];
       end
       end
       else raise ENotSupportedException.CreateResFmt(@SUnRecognizedStartCharacter, [LPayloadStartChar]);
       else raise ENotSupportedException.CreateResFmt(@SUnRecognizedStartCharacter, [LPayloadStartChar]);
   end;
   end;
 
 
   // Password
   // Password
-  if (AEPasa.PayloadTypes.IsPayloadTypeInSet(PayloadType.PasswordEncrypted)) then begin
+  if (AEPasa.PayloadType.HasTrait(ptPasswordEncrypted)) then begin
     if (LPayloadPasswordDelim = #0) then begin
     if (LPayloadPasswordDelim = #0) then begin
       AErrorCode := EPasaErrorCode.MissingPassword;
       AErrorCode := EPasaErrorCode.MissingPassword;
       Exit(False);
       Exit(False);
@@ -551,27 +675,27 @@ begin
     if (LPayloadContent = #0) then begin
     if (LPayloadContent = #0) then begin
       AEPasa.Payload := string.Empty;
       AEPasa.Payload := string.Empty;
     end else if (LPayloadContent.StartsWith('"')) then begin
     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(['"']));
       AEPasa.Payload := TPascalAsciiEncoding.UnEscape(LPayloadContent.Trim(['"']));
     end else if (LPayloadContent.StartsWith('0x')) then begin
     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);
       AEPasa.Payload := System.Copy(LPayloadContent, 3, LPayloadContent.Length - 2);
     end else begin
     end else begin
-      AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.Base58Formatted] - [PayloadType.NonDeterministic];
+      AEPasa.PayloadType := AEPasa.PayloadType + [ptBase58Formatted] - [ptNonDeterministic];
       AEPasa.Payload := LPayloadContent;
       AEPasa.Payload := LPayloadContent;
     end;
     end;
   end;
   end;
 
 
   // Payload Lengths
   // 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;
     AErrorCode := EPasaErrorCode.PayloadTooLarge;
     Exit(False);
     Exit(False);
   end;
   end;
 
 
   // Extended Checksum
   // Extended Checksum
-  LActualChecksum := TEPasaHelper.ComputeExtendedChecksum(AEPasa.ToString(True));
+  LActualChecksum := TEPasaComp.ComputeExtendedChecksum(AEPasa.ToString(True));
   if (LExtendedChecksumDelim <> #0) then begin
   if (LExtendedChecksumDelim <> #0) then begin
-    if (LExtendedChecksum <> LActualChecksum) then begin
+    if (LExtendedChecksum <> LActualChecksum) and (Not AOmitExtendedChecksumVerification) then begin
       AErrorCode := EPasaErrorCode.BadExtendedChecksum;
       AErrorCode := EPasaErrorCode.BadExtendedChecksum;
       Exit(False);
       Exit(False);
     end;
     end;
@@ -582,16 +706,16 @@ begin
 end;
 end;
 
 
 
 
-{ TEPasaHelper }
+{ TEPasaComp }
 
 
-class function TEPasaHelper.ReadUInt16AsBytesLE(AValue: UInt16): TArray<Byte>;
+class function TEPasaComp.ReadUInt16AsBytesLE(AValue: UInt16): TArray<Byte>;
 begin
 begin
   System.SetLength(Result, System.SizeOf(UInt16));
   System.SetLength(Result, System.SizeOf(UInt16));
   Result[0] := Byte(AValue);
   Result[0] := Byte(AValue);
   Result[1] := Byte(AValue shr 8);
   Result[1] := Byte(AValue shr 8);
 end;
 end;
 
 
-class function TEPasaHelper.ComputeExtendedChecksum(const AText: String): String;
+class function TEPasaComp.ComputeExtendedChecksum(const AText: String): String;
 var
 var
   LHashInstance: IHashWithKey;
   LHashInstance: IHashWithKey;
   LChecksum: UInt16;
   LChecksum: UInt16;
@@ -602,53 +726,53 @@ begin
   Result := THexEncoding.Encode(ReadUInt16AsBytesLE(LChecksum), True);
   Result := THexEncoding.Encode(ReadUInt16AsBytesLE(LChecksum), True);
 end;
 end;
 
 
-class function TEPasaHelper.IsValidExtendedChecksum(const AText, AChecksum: String): Boolean;
+class function TEPasaComp.IsValidExtendedChecksum(const AText, AChecksum: String): Boolean;
 begin
 begin
   Result := ComputeExtendedChecksum(AText) = AChecksum;
   Result := ComputeExtendedChecksum(AText) = AChecksum;
 end;
 end;
 
 
-class function TEPasaHelper.IsValidPayloadLength(APayloadType: PayloadTypes; const APayloadContent: String): Boolean;
+class function TEPasaComp.IsValidPayloadLength(APayloadType: TPayloadType; const APayloadContent: String): Boolean;
 begin
 begin
   if (string.IsNullOrEmpty(APayloadContent)) then
   if (string.IsNullOrEmpty(APayloadContent)) then
     Exit(True);
     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);
       Exit(TPascalAsciiEncoding.UnEscape(APayloadContent).Length <= MaxPublicAsciiContentLength);
 
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.HexFormatted)) then
+    if (APayloadType.HasTrait(ptHexFormatted)) then
       Exit(APayloadContent.Length <= MaxPublicHexContentLength);
       Exit(APayloadContent.Length <= MaxPublicHexContentLength);
 
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.Base58Formatted)) then
+    if (APayloadType.HasTrait(ptBase58Formatted)) then
       Exit(APayloadContent.Length <= MaxPublicBase58ContentLength);
       Exit(APayloadContent.Length <= MaxPublicBase58ContentLength);
 
 
     // unknown encoding format
     // unknown encoding format
     Result := False;
     Result := False;
   end;
   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);
       Exit(TPascalAsciiEncoding.UnEscape(APayloadContent).Length <= MaxECIESAsciiContentLength);
 
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.HexFormatted)) then
+    if (APayloadType.HasTrait(ptHexFormatted)) then
       Exit(APayloadContent.Length <= MaxECIESHexContentLength);
       Exit(APayloadContent.Length <= MaxECIESHexContentLength);
 
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.Base58Formatted)) then
+    if (APayloadType.HasTrait(ptBase58Formatted)) then
       Exit(APayloadContent.Length <= MaxECIESBase58ContentLength);
       Exit(APayloadContent.Length <= MaxECIESBase58ContentLength);
     // unknown encoding format
     // unknown encoding format
     Result := False;
     Result := False;
   end;
   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);
       Exit(TPascalAsciiEncoding.UnEscape(APayloadContent).Length <= MaxAESAsciiContentLength);
 
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.HexFormatted)) then
+    if (APayloadType.HasTrait(ptHexFormatted)) then
       Exit(APayloadContent.Length <= MaxAESHexContentLength);
       Exit(APayloadContent.Length <= MaxAESHexContentLength);
 
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.Base58Formatted)) then
+    if (APayloadType.HasTrait(ptBase58Formatted)) then
       Exit(APayloadContent.Length <= MaxAESBase58ContentLength);
       Exit(APayloadContent.Length <= MaxAESBase58ContentLength);
 
 
     // unknown encoding format
     // unknown encoding format
@@ -659,10 +783,61 @@ begin
   Result := False;
   Result := False;
 end;
 end;
 
 
-class function TEPasaHelper.IsValidPasswordLength(const APasswordValue : String): Boolean;
+class function TEPasaComp.IsValidPasswordLength(const APasswordValue : String): Boolean;
 begin
 begin
   // no password length policy established (only client-side concern)
   // no password length policy established (only client-side concern)
   Result := True;
   Result := True;
 end;
 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.
 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
     public
       const
       const
         CharSet = '0123456789abcdef';
         CharSet = '0123456789abcdef';
-        NibblePattern = '[0-9a-f]';
+        NibblePattern = '[0-9a-fA-F]';
         BytePattern = NibblePattern + '{2}';
         BytePattern = NibblePattern + '{2}';
         SubStringPattern = '(?:' + BytePattern + ')+';
         SubStringPattern = '(?:' + BytePattern + ')+';
         StringPattern = SubStringPattern + '$';
         StringPattern = SubStringPattern + '$';

+ 16 - 11
src/core/UNetProtocol.pas

@@ -420,7 +420,6 @@ Type
     Procedure DoProcess_GetPendingOperations;
     Procedure DoProcess_GetPendingOperations;
     Procedure SetClient(Const Value : TNetTcpIpClient);
     Procedure SetClient(Const Value : TNetTcpIpClient);
     Function ReadTcpClientBuffer(MaxWaitMiliseconds : Cardinal; var HeaderData : TNetHeaderData; BufferData : TStream) : Boolean;
     Function ReadTcpClientBuffer(MaxWaitMiliseconds : Cardinal; var HeaderData : TNetHeaderData; BufferData : TStream) : Boolean;
-    Procedure DisconnectInvalidClient(ItsMyself : Boolean; Const why : String);
     function GetClient: TNetTcpIpClient;
     function GetClient: TNetTcpIpClient;
   protected
   protected
     Procedure Send(NetTranferType : TNetTransferType; operation, errorcode : Word; request_id : Integer; DataBuffer : TStream);
     Procedure Send(NetTranferType : TNetTransferType; operation, errorcode : Word; request_id : Integer; DataBuffer : TStream);
@@ -434,6 +433,7 @@ Type
     Function ConnectTo(ServerIP: String; ServerPort:Word) : Boolean;
     Function ConnectTo(ServerIP: String; ServerPort:Word) : Boolean;
     Property Connected : Boolean read GetConnected write SetConnected;
     Property Connected : Boolean read GetConnected write SetConnected;
     Property IsConnecting : Boolean read FIsConnecting;
     Property IsConnecting : Boolean read FIsConnecting;
+    Procedure DisconnectInvalidClient(ItsMyself : Boolean; Const why : String);
     Function Send_Hello(NetTranferType : TNetTransferType; request_id : Integer) : Boolean;
     Function Send_Hello(NetTranferType : TNetTransferType; request_id : Integer) : Boolean;
     Function Send_NewBlockFound(Const NewBlock : TPCOperationsComp) : Boolean;
     Function Send_NewBlockFound(Const NewBlock : TPCOperationsComp) : Boolean;
     Function Send_GetBlocks(StartAddress, quantity : Cardinal; var request_id : Cardinal) : 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);
 procedure TNetConnection.DoProcess_GetPubkeyAccounts_Request(HeaderData: TNetHeaderData; DataBuffer: TStream);
 Const CT_Max_Accounts_per_call = 1000;
 Const CT_Max_Accounts_per_call = 1000;
 var responseStream, accountsStream : TMemoryStream;
 var responseStream, accountsStream : TMemoryStream;
-  start,max : Integer;
+  start,max,i : Integer;
   c, nAccounts : Cardinal;
   c, nAccounts : Cardinal;
   acc : TAccount;
   acc : TAccount;
   DoDisconnect : Boolean;
   DoDisconnect : Boolean;
@@ -3405,6 +3405,7 @@ var responseStream, accountsStream : TMemoryStream;
   pubKey : TAccountKey;
   pubKey : TAccountKey;
   sbakl : TSafeboxPubKeysAndAccounts;
   sbakl : TSafeboxPubKeysAndAccounts;
   ocl : TAccountsNumbersList;
   ocl : TAccountsNumbersList;
+  LAccountsList : TList<Integer>;
 begin
 begin
   {
   {
   This call is used to obtain Accounts used by a Public key
   This call is used to obtain Accounts used by a Public key
@@ -3451,15 +3452,19 @@ begin
     if Assigned(sbakl) then begin
     if Assigned(sbakl) then begin
       ocl := sbakl.GetAccountsUsingThisKey(pubKey);
       ocl := sbakl.GetAccountsUsingThisKey(pubKey);
       if Assigned(ocl) then begin
       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;
       end;
       end;
       // Save & send
       // Save & send

+ 239 - 3
src/core/UNode.pas

@@ -35,8 +35,8 @@ interface
 
 
 uses
 uses
   Classes, SysUtils,
   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}
 {$I ./../config.inc}
 
 
@@ -64,6 +64,7 @@ Type
     FSentOperations : TOrderedRawList;
     FSentOperations : TOrderedRawList;
     FBroadcastData : Boolean;
     FBroadcastData : Boolean;
     FUpdateBlockchain: Boolean;
     FUpdateBlockchain: Boolean;
+    FMaxPayToKeyPurchasePrice: Int64;
     {$IFDEF BufferOfFutureOperations}
     {$IFDEF BufferOfFutureOperations}
     FBufferAuxWaitingOperations : TOperationsHashTree;
     FBufferAuxWaitingOperations : TOperationsHashTree;
     {$ENDIF}
     {$ENDIF}
@@ -119,12 +120,24 @@ Type
     function TryLockNode(MaxWaitMilliseconds : Cardinal) : Boolean;
     function TryLockNode(MaxWaitMilliseconds : Cardinal) : Boolean;
     procedure UnlockNode;
     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 BroadcastData : Boolean read FBroadcastData write FBroadcastData;
     Property UpdateBlockchain : Boolean read FUpdateBlockchain write FUpdateBlockchain;
     Property UpdateBlockchain : Boolean read FUpdateBlockchain write FUpdateBlockchain;
     procedure MarkVerifiedECDSASignaturesFromMemPool(newOperationsToValidate : TPCOperationsComp);
     procedure MarkVerifiedECDSASignaturesFromMemPool(newOperationsToValidate : TPCOperationsComp);
     class function NodeVersion : String;
     class function NodeVersion : String;
     class function GetPascalCoinDataFolder : String;
     class function GetPascalCoinDataFolder : String;
     class procedure SetPascalCoinDataFolder(const ANewDataFolder : 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;
   End;
 
 
   TThreadSafeNodeNotifyEvent = Class(TPCThread)
   TThreadSafeNodeNotifyEvent = Class(TPCThread)
@@ -205,7 +218,8 @@ Type
 
 
 implementation
 implementation
 
 
-Uses UOpTransaction, UConst, UTime, UCommon, UPCOperationsSignatureValidator, UFolderHelper;
+Uses UOpTransaction, UConst, UTime, UCommon, UPCOperationsSignatureValidator,
+  UFolderHelper, USettings;
 
 
 var _Node : TNode;
 var _Node : TNode;
   _PascalCoinDataFolder : String;
   _PascalCoinDataFolder : String;
@@ -630,6 +644,7 @@ end;
 
 
 constructor TNode.Create(AOwner: TComponent);
 constructor TNode.Create(AOwner: TComponent);
 begin
 begin
+  FMaxPayToKeyPurchasePrice := 0;
   FSentOperations := TOrderedRawList.Create;
   FSentOperations := TOrderedRawList.Create;
   FNodeLog := TLog.Create(Self);
   FNodeLog := TLog.Create(Self);
   FNodeLog.ProcessGlobalLogs := false;
   FNodeLog.ProcessGlobalLogs := false;
@@ -762,6 +777,170 @@ begin
   dec(FDisabledsNewBlocksCount);
   dec(FDisabledsNewBlocksCount);
 end;
 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;
 function TNode.TryLockNode(MaxWaitMilliseconds: Cardinal): Boolean;
 begin
 begin
   Result := TPCThread.TryProtectEnterCriticalSection(Self,MaxWaitMilliseconds,FLockMempool);
   Result := TPCThread.TryProtectEnterCriticalSection(Self,MaxWaitMilliseconds,FLockMempool);
@@ -1285,6 +1464,63 @@ begin
   end;
   end;
 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;
 function TNode.GetMempoolAccount(AAccountNumber : Cardinal): TAccount;
 var LLockedMempool : TPCOperationsComp;
 var LLockedMempool : TPCOperationsComp;
 begin
 begin

+ 1 - 1
src/core/UOpTransaction.pas

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

+ 182 - 229
src/core/UPCAbstractMem.pas

@@ -6,12 +6,16 @@ interface
   {$MODE DELPHI}
   {$MODE DELPHI}
 {$ENDIF}
 {$ENDIF}
 
 
+{$I ./../config.inc}
+
 uses Classes, SysUtils, SyncObjs,
 uses Classes, SysUtils, SyncObjs,
   UAbstractMem, UFileMem, UAbstractMemTList, UCacheMem,
   UAbstractMem, UFileMem, UAbstractMemTList, UCacheMem,
-  UAbstractBTree, UThread,
+  UAbstractBTree, UThread, UAbstractMemBTree,
   UAVLCache, ULog, UCrypto,
   UAVLCache, ULog, UCrypto,
   UPCAbstractMemAccountKeys,
   UPCAbstractMemAccountKeys,
   UPCDataTypes, UBaseTypes, UConst, UPCSafeBoxRootHash, UOrderedList,
   UPCDataTypes, UBaseTypes, UConst, UPCSafeBoxRootHash, UOrderedList,
+  UPCAccountsOrdenations,
+  UPCAbstractMemAccounts,
 {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults{$ELSE}Generics.Collections,Generics.Defaults{$ENDIF};
 {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults{$ELSE}Generics.Collections,Generics.Defaults{$ENDIF};
 
 
 type
 type
@@ -34,8 +38,6 @@ type
     procedure SaveTo(const AItem: TOperationBlockExt; AIsAddingItem : Boolean; var ABytes: TBytes); override;
     procedure SaveTo(const AItem: TOperationBlockExt; AIsAddingItem : Boolean; var ABytes: TBytes); override;
   end;
   end;
 
 
-  TPCAbstractMemListAccounts = class;
-
   TAccountNameInfo = record
   TAccountNameInfo = record
     accountName: string;
     accountName: string;
     accountNumber: cardinal;
     accountNumber: cardinal;
@@ -43,30 +45,22 @@ type
 
 
   { TPCAbstractMemListAccountNames }
   { TPCAbstractMemListAccountNames }
 
 
-  TPCAbstractMemListAccountNames = class(TAbstractMemOrderedTList<TAccountNameInfo>)
+  TPCAbstractMemListAccountNames = Class(TAbstractMemBTreeData<TAccountNameInfo>)
   private
   private
     FPCAbstractMem: TPCAbstractMem;
     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
   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
   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;
   end;
 
 
   { TPCAbstractMemBytesBuffer32Safebox }
   { TPCAbstractMemBytesBuffer32Safebox }
@@ -128,6 +122,8 @@ type
 
 
     FSavingOldGridCache : Boolean;
     FSavingOldGridCache : Boolean;
     FSavingOldDefaultCacheDataBlocksSize : Integer;
     FSavingOldDefaultCacheDataBlocksSize : Integer;
+    FAccountsOrderedByUpdatedBlock : TAccountsOrderedByUpdatedBlock;
+    FAccountsOrderedBySalePrice : TAccountsOrderedBySalePrice;
 
 
     function IsChecking : Boolean;
     function IsChecking : Boolean;
     procedure DoCheck;
     procedure DoCheck;
@@ -145,6 +141,7 @@ type
     procedure SetSavingNewSafeboxMode(const Value: Boolean);
     procedure SetSavingNewSafeboxMode(const Value: Boolean);
   protected
   protected
     procedure UpgradeAbstractMemVersion(const ACurrentHeaderVersion : Integer);
     procedure UpgradeAbstractMemVersion(const ACurrentHeaderVersion : Integer);
+    function DoGetAccount(AAccountNumber : Integer; var AAccount : TAccount) : Boolean;
   public
   public
     constructor Create(const ASafeboxFileName: string; AReadOnly: boolean);
     constructor Create(const ASafeboxFileName: string; AReadOnly: boolean);
     class function AnalyzeFile(const ASafeboxFileName: string; var ABlocksCount : Integer) : Boolean;
     class function AnalyzeFile(const ASafeboxFileName: string; var ABlocksCount : Integer) : Boolean;
@@ -185,6 +182,8 @@ type
     Property MaxAccountsCache : Integer read GetMaxAccountsCache write SetMaxAccountsCache;
     Property MaxAccountsCache : Integer read GetMaxAccountsCache write SetMaxAccountsCache;
     Property MaxAccountKeysCache : Integer read GetMaxAccountKeysCache write SetMaxAccountKeysCache;
     Property MaxAccountKeysCache : Integer read GetMaxAccountKeysCache write SetMaxAccountKeysCache;
     Property SavingNewSafeboxMode : Boolean read FSavingNewSafeboxMode write SetSavingNewSafeboxMode;
     Property SavingNewSafeboxMode : Boolean read FSavingNewSafeboxMode write SetSavingNewSafeboxMode;
+    Property AccountsOrderedByUpdatedBlock : TAccountsOrderedByUpdatedBlock read FAccountsOrderedByUpdatedBlock;
+    Property AccountsOrderedBySalePrice : TAccountsOrderedBySalePrice read FAccountsOrderedBySalePrice;
   end;
   end;
 
 
 implementation
 implementation
@@ -192,8 +191,8 @@ implementation
 uses UAccounts;
 uses UAccounts;
 
 
 const
 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;
 function _AccountCache_Comparision(const Left, Right: TAccountCache.PAVLCacheMemData): Integer;
 begin
 begin
@@ -260,145 +259,6 @@ begin
   end;
   end;
 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 }
 { TPCAbstractMem }
 
 
 function TPCAbstractMem.CheckConsistency(AReport: TStrings) : Boolean;
 function TPCAbstractMem.CheckConsistency(AReport: TStrings) : Boolean;
@@ -437,6 +297,11 @@ begin
   DoInit(LIsNew);
   DoInit(LIsNew);
 end;
 end;
 
 
+function _TComparison_TAccountNameInfo(const ALeft, ARight : TAccountNameInfo) : Integer;
+begin
+  Result := CompareText(ALeft.accountName,ARight.accountName);
+end;
+
 function TPCAbstractMem.DoInit(out AIsNewStructure : Boolean) : Boolean;
 function TPCAbstractMem.DoInit(out AIsNewStructure : Boolean) : Boolean;
 const
 const
   CT_HEADER_MIN_SIZE = 100;
   CT_HEADER_MIN_SIZE = 100;
@@ -451,6 +316,8 @@ const
   [28..31] 4 bytes: LZoneAccountKeys.position
   [28..31] 4 bytes: LZoneAccountKeys.position
   [32..35] 4 bytes: FZoneAggregatedHashrate.position
   [32..35] 4 bytes: FZoneAggregatedHashrate.position
   [36..39] 4 bytes: LZoneBuffersBlockHash
   [36..39] 4 bytes: LZoneBuffersBlockHash
+  [40..43] 4 bytes: LZoneAccountsOrderedByUpdatedBlock.position
+  [44..47] 4 bytes: LZoneAccountsOrderedBySalePrice.position
   ...
   ...
   [96..99] 4 bytes: Header version
   [96..99] 4 bytes: Header version
   }
   }
@@ -458,7 +325,9 @@ var LZone,
   LZoneBlocks,
   LZoneBlocks,
   LZoneAccounts,
   LZoneAccounts,
   LZoneAccountsNames,
   LZoneAccountsNames,
-  LZoneAccountKeys : TAMZone;
+  LZoneAccountKeys,
+  LZoneAccountsOrderedByUpdatedBlock,
+  LZoneAccountsOrderedBySalePrice : TAMZone;
   LZoneBuffersBlockHash : TAbstractMemPosition;
   LZoneBuffersBlockHash : TAbstractMemPosition;
   LHeader, LBuffer, LBigNum : TBytes;
   LHeader, LBuffer, LBigNum : TBytes;
   LIsGood : Boolean;
   LIsGood : Boolean;
@@ -472,6 +341,8 @@ begin
   FreeAndNil(FAccountsNames);
   FreeAndNil(FAccountsNames);
   FreeAndNil(FAccountKeys);
   FreeAndNil(FAccountKeys);
   FreeAndNil(FBufferBlocksHash);
   FreeAndNil(FBufferBlocksHash);
+  FreeAndNil(FAccountsOrderedByUpdatedBlock);
+  FreeAndNil(FAccountsOrderedBySalePrice);
   //
   //
   Result := False;
   Result := False;
   AIsNewStructure := True;
   AIsNewStructure := True;
@@ -482,6 +353,8 @@ begin
   LZoneAccountKeys.Clear;
   LZoneAccountKeys.Clear;
   FZoneAggregatedHashrate.Clear;
   FZoneAggregatedHashrate.Clear;
   LZoneBuffersBlockHash := 0;
   LZoneBuffersBlockHash := 0;
+  LZoneAccountsOrderedByUpdatedBlock.Clear;
+  LZoneAccountsOrderedBySalePrice.Clear;
 
 
   if (FAbstractMem.ReadFirstData(LZone,LHeader)) then begin
   if (FAbstractMem.ReadFirstData(LZone,LHeader)) then begin
     // Check if header is valid:
     // Check if header is valid:
@@ -504,11 +377,27 @@ begin
         Move(LHeader[28], LZoneAccountKeys.position, 4);
         Move(LHeader[28], LZoneAccountKeys.position, 4);
         Move(LHeader[32], FZoneAggregatedHashrate.position, 4);
         Move(LHeader[32], FZoneAggregatedHashrate.position, 4);
         LZoneBuffersBlockHash := LZone.position + 36;
         LZoneBuffersBlockHash := LZone.position + 36;
+        Move(LHeader[40], LZoneAccountsOrderedByUpdatedBlock.position, 4);
+        Move(LHeader[44], LZoneAccountsOrderedBySalePrice.position, 4);
+        //
         Move(LHeader[96], LHeaderVersion, 4);
         Move(LHeader[96], LHeaderVersion, 4);
         if (LHeaderVersion>CT_PCAbstractMem_HeaderVersion) then begin
         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]));
           TLog.NewLog(lterror,ClassName,Format('Header version readed %d is greater than expected %d',[LHeaderVersion,CT_PCAbstractMem_HeaderVersion]));
         end else begin
         end else begin
           AIsNewStructure := False;
           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;
       end;
     end;
     end;
@@ -530,6 +419,10 @@ begin
     LZoneAccountKeys := FAbstractMem.New( 100 );
     LZoneAccountKeys := FAbstractMem.New( 100 );
     FZoneAggregatedHashrate := FAbstractMem.New(100); // Note: Enough big to store a BigNum
     FZoneAggregatedHashrate := FAbstractMem.New(100); // Note: Enough big to store a BigNum
     LZoneBuffersBlockHash := LZone.position+36;
     LZoneBuffersBlockHash := LZone.position+36;
+    LZoneAccountsOrderedByUpdatedBlock := FAbstractMem.New(
+      TAbstractMemBTree.MinAbstractMemInitialPositionSize);
+    LZoneAccountsOrderedBySalePrice := FAbstractMem.New(
+      TAbstractMemBTree.MinAbstractMemInitialPositionSize);
 
 
     Move(LZoneBlocks.position,       LHeader[16],4);
     Move(LZoneBlocks.position,       LHeader[16],4);
     Move(LZoneAccounts.position,     LHeader[20],4);
     Move(LZoneAccounts.position,     LHeader[20],4);
@@ -537,6 +430,8 @@ begin
     Move(LZoneAccountKeys.position,  LHeader[28],4);
     Move(LZoneAccountKeys.position,  LHeader[28],4);
     Move(FZoneAggregatedHashrate.position,LHeader[32],4);
     Move(FZoneAggregatedHashrate.position,LHeader[32],4);
     LHeaderVersion := CT_PCAbstractMem_HeaderVersion;
     LHeaderVersion := CT_PCAbstractMem_HeaderVersion;
+    Move(LZoneAccountsOrderedByUpdatedBlock, LHeader[40],4);
+    Move(LZoneAccountsOrderedBySalePrice, LHeader[44],4);
     Move(LHeaderVersion,             LHeader[96],4);
     Move(LHeaderVersion,             LHeader[96],4);
 
 
     FAbstractMem.Write(LZone.position,LHeader[0],Length(LHeader));
     FAbstractMem.Write(LZone.position,LHeader[0],Length(LHeader));
@@ -549,12 +444,12 @@ begin
   FBlocks.FPCAbstractMem := Self;
   FBlocks.FPCAbstractMem := Self;
 
 
   FAccounts := TPCAbstractMemListAccounts.Create( FAbstractMem, LZoneAccounts, 100000, Self.UseCacheOnAbstractMemLists);
   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;
   FAccountsNames.FPCAbstractMem := Self;
 
 
   FAccountKeys := TPCAbstractMemAccountKeys.Create( FAbstractMem, LZoneAccountKeys.position, Self.UseCacheOnAbstractMemLists);
   FAccountKeys := TPCAbstractMemAccountKeys.Create( FAbstractMem, LZoneAccountKeys.position, Self.UseCacheOnAbstractMemLists);
+  FAccounts.AccountKeys := FAccountKeys;
 
 
   // Read AggregatedHashrate
   // Read AggregatedHashrate
   SetLength(LBuffer,100);
   SetLength(LBuffer,100);
@@ -564,6 +459,12 @@ begin
   end;
   end;
   FBufferBlocksHash := TPCAbstractMemBytesBuffer32Safebox.Create(FAbstractMem,LZoneBuffersBlockHash,FBlocks.Count);
   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;
   FAccountCache.Clear;
 
 
   if (Not AIsNewStructure) And (Not FAbstractMem.ReadOnly) And (LHeaderVersion<CT_PCAbstractMem_HeaderVersion) then begin
   if (Not AIsNewStructure) And (Not FAbstractMem.ReadOnly) And (LHeaderVersion<CT_PCAbstractMem_HeaderVersion) then begin
@@ -645,6 +546,8 @@ begin
   FreeAndNil(FAccountKeys);
   FreeAndNil(FAccountKeys);
   FreeAndNil(FBufferBlocksHash);
   FreeAndNil(FBufferBlocksHash);
   FreeAndNil(FAggregatedHashrate);
   FreeAndNil(FAggregatedHashrate);
+  FreeAndNil(FAccountsOrderedByUpdatedBlock);
+  FreeAndNil(FAccountsOrderedBySalePrice);
   if (FFileName<>'') And (FAbstractMem is TMem) And (Not FAbstractMem.ReadOnly) then begin
   if (FFileName<>'') And (FAbstractMem is TMem) And (Not FAbstractMem.ReadOnly) then begin
     LFile := TFileStream.Create(FFileName,fmCreate);
     LFile := TFileStream.Create(FFileName,fmCreate);
     try
     try
@@ -676,6 +579,12 @@ begin
   End;
   End;
 end;
 end;
 
 
+function TPCAbstractMem.DoGetAccount(AAccountNumber: Integer; var AAccount: TAccount): Boolean;
+begin
+  AAccount := GetAccount(AAccountNumber);
+  Result := True;
+end;
+
 procedure TPCAbstractMem.FlushCache;
 procedure TPCAbstractMem.FlushCache;
 var LBigNum : TBytes;
 var LBigNum : TBytes;
   Ltc : TTickCount;
   Ltc : TTickCount;
@@ -684,7 +593,6 @@ begin
   Ltc := TPlatform.GetTickCount;
   Ltc := TPlatform.GetTickCount;
   FBlocks.FlushCache;
   FBlocks.FlushCache;
   FAccounts.FlushCache;
   FAccounts.FlushCache;
-  FAccountsNames.FlushCache;
   FAccountKeys.FlushCache;
   FAccountKeys.FlushCache;
   FBufferBlocksHash.Flush;
   FBufferBlocksHash.Flush;
   LBigNum := FAggregatedHashrate.RawValue.ToSerialized;
   LBigNum := FAggregatedHashrate.RawValue.ToSerialized;
@@ -743,13 +651,13 @@ begin
   if (AAccount.account<0) or (AAccount.account>FAccounts.Count) then 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]));
     raise EPCAbstractMem.Create(Format('Account %d not in range %d..%d',[AAccount.account,0,FAccounts.Count]));
   end;
   end;
-  FAccountCache.Remove(AAccount);
   if (AAccount.account = FAccounts.Count) then begin
   if (AAccount.account = FAccounts.Count) then begin
     FAccounts.Add(AAccount);
     FAccounts.Add(AAccount);
   end else begin
   end else begin
-    FAccounts.SetItem( AAccount.account , AAccount);
+    FAccounts.Item[ AAccount.account ] := AAccount;
   end;
   end;
   // Update cache
   // Update cache
+  FAccountCache.Remove(AAccount);
   FAccountCache.Add(AAccount);
   FAccountCache.Add(AAccount);
 end;
 end;
 
 
@@ -903,24 +811,17 @@ procedure TPCAbstractMem.UpgradeAbstractMemVersion(const ACurrentHeaderVersion:
 var LFirstTC, LTC : TTickCount;
 var LFirstTC, LTC : TTickCount;
   i : integer;
   i : integer;
   LAccount : TAccount;
   LAccount : TAccount;
+  LaccInfoNul : TAccountInfo;
 begin
 begin
   LFirstTC := TPlatform.GetTickCount;
   LFirstTC := TPlatform.GetTickCount;
   LTC := LFirstTC;
   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
     for i := 0 to AccountsCount-1 do begin
       LAccount := GetAccount(i);
       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;
     end;
-    TLog.NewLog(ltdebug,ClassName,Format('End upgrade found %d',[AccountsNames.Count]));
   end;
   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]));
   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;
 end;
@@ -1003,25 +904,25 @@ begin
   Result.Clear;
   Result.Clear;
   Result.account := AAccountNumber;
   Result.account := AAccountNumber;
   if Not FAccountCache.Find(Result,Result) then begin
   if Not FAccountCache.Find(Result,Result) then begin
-    Result := FAccounts.GetItem( AAccountNumber );
+    Result := FAccounts.Item[ AAccountNumber ];
     // Save for future usage:
     // Save for future usage:
     FAccountCache.Add(Result);
     FAccountCache.Add(Result);
   end;
   end;
 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
 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;
 end;
 
 
 procedure TPCAbstractMemListAccountNames.LoadFrom(const ABytes: TBytes; var AItem: TAccountNameInfo);
 procedure TPCAbstractMemListAccountNames.LoadFrom(const ABytes: TBytes; var AItem: TAccountNameInfo);
@@ -1032,14 +933,31 @@ begin
   Move(ABytes[LTmp.GetSerializedLength],AItem.accountNumber,4);
   Move(ABytes[LTmp.GetSerializedLength],AItem.accountNumber,4);
 end;
 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
 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;
 end;
 
 
-procedure TPCAbstractMemListAccountNames.SaveTo(const AItem: TAccountNameInfo; AIsAddingItem : Boolean; var ABytes: TBytes);
+procedure TPCAbstractMemListAccountNames.SaveTo(const AItem: TAccountNameInfo; var ABytes: TBytes);
 var LStream : TStream;
 var LStream : TStream;
   LTmp : TBytes;
   LTmp : TBytes;
 begin
 begin
@@ -1055,36 +973,46 @@ begin
   End;
   End;
 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
 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]))
       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,
     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;
 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
 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;
 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
 begin
-  LFind.accountName := AName;
-  LFind.accountNumber := 0;
-  Result := Find(LFind,AIndex);
+  Lani.accountName := AName;
+  Lani.accountNumber := 0;
+  Result := FindData(Lani,AAbstractMemPosition);
 end;
 end;
 
 
 { TPCAbstractMemListBlocks }
 { TPCAbstractMemListBlocks }
@@ -1166,15 +1094,18 @@ procedure TPCAbstractMemCheckThread.BCExecute;
     inc(FErrorsCount);
     inc(FErrorsCount);
     TLog.NewLog(ltError,ClassName,'CheckConsistency: '+AError);
     TLog.NewLog(ltError,ClassName,'CheckConsistency: '+AError);
   end;
   end;
-var iBlock, i, iAccName : Integer;
+var iBlock, i : Integer;
   LAccount : TAccount;
   LAccount : TAccount;
   LBlockAccount : TBlockAccount;
   LBlockAccount : TBlockAccount;
+  LHighestOperationBlock : TOperationBlockExt;
   LOrdered : TOrderedList<Integer>;
   LOrdered : TOrderedList<Integer>;
   LOrderedNames : TOrderedList<String>;
   LOrderedNames : TOrderedList<String>;
-  LAccountNameInfo : TAccountNameInfo;
   LTC, LTCInitial : TTickCount;
   LTC, LTCInitial : TTickCount;
   LAggregatedHashrate, LBlockHashRate : TBigNum;
   LAggregatedHashrate, LBlockHashRate : TBigNum;
+  LBuff1,LBuff2 : TRawBytes;
+  Laninfo : TAccountNameInfo;
 begin
 begin
+  LBlockAccount := CT_BlockAccount_NUL;
   iBlock :=0;
   iBlock :=0;
   LOrdered := TOrderedList<Integer>.Create(False,TComparison_Integer);
   LOrdered := TOrderedList<Integer>.Create(False,TComparison_Integer);
   LOrderedNames := TOrderedList<String>.Create(False,TComparison_String);
   LOrderedNames := TOrderedList<String>.Create(False,TComparison_String);
@@ -1182,6 +1113,7 @@ begin
   Try
   Try
     LTC := TPlatform.GetTickCount;
     LTC := TPlatform.GetTickCount;
     LTCInitial := LTC;
     LTCInitial := LTC;
+    LHighestOperationBlock := FPCAbstractMem.GetBlockInfo(FPCAbstractMem.BlocksCount-1);
     while (iBlock < FPCAbstractMem.BlocksCount) and (Not Terminated) do begin
     while (iBlock < FPCAbstractMem.BlocksCount) and (Not Terminated) do begin
       if FMustRestart then begin
       if FMustRestart then begin
         TLog.NewLog(ltdebug,ClassName,Format('Restarting check thread after %d/%d blocks',[iBlock+1,FPCAbstractMem.BlocksCount]) );
         TLog.NewLog(ltdebug,ClassName,Format('Restarting check thread after %d/%d blocks',[iBlock+1,FPCAbstractMem.BlocksCount]) );
@@ -1192,6 +1124,7 @@ begin
         LOrdered.Clear;
         LOrdered.Clear;
         LOrderedNames.Clear;
         LOrderedNames.Clear;
         LAggregatedHashrate.Value := 0;
         LAggregatedHashrate.Value := 0;
+        LHighestOperationBlock := FPCAbstractMem.GetBlockInfo(FPCAbstractMem.BlocksCount-1);
       end;
       end;
 
 
       LBlockAccount := FPCAbstractMem.GetBlockAccount(iBlock);
       LBlockAccount := FPCAbstractMem.GetBlockAccount(iBlock);
@@ -1202,19 +1135,30 @@ begin
           if LOrderedNames.Add(LAccount.name.ToString)<0 then begin
           if LOrderedNames.Add(LAccount.name.ToString)<0 then begin
             _error(Format('Account %d name %s allready added',[LAccount.account,LAccount.name.ToString]));
             _error(Format('Account %d name %s allready added',[LAccount.account,LAccount.name.ToString]));
           end;
           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
             _error(Format('Account %d name %s not found at list',[LAccount.account,LAccount.name.ToString]));
             _error(Format('Account %d name %s not found at list',[LAccount.account,LAccount.name.ToString]));
           end else begin
           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;
             end;
             if (LOrdered.Add(LAccount.account)<0) then begin
             if (LOrdered.Add(LAccount.account)<0) then begin
               _error(Format('Account %d (with name %s) allready added',[LAccount.account,LAccount.name.ToString]));
               _error(Format('Account %d (with name %s) allready added',[LAccount.account,LAccount.name.ToString]));
             end;
             end;
           end;
           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;
       end;
 
 
       LBlockHashRate := TBigNum.TargetToHashRate( LBlockAccount.blockchainInfo.compact_target );
       LBlockHashRate := TBigNum.TargetToHashRate( LBlockAccount.blockchainInfo.compact_target );
@@ -1231,12 +1175,21 @@ begin
       inc(iBlock);
       inc(iBlock);
     end;
     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;
     end;
+
     if (LOrdered.Count)<>FPCAbstractMem.AccountsNames.Count then begin
     if (LOrdered.Count)<>FPCAbstractMem.AccountsNames.Count then begin
       _error(Format('Found %d accounts with names but %d on list',[LOrdered.Count,FPCAbstractMem.AccountsNames.Count]));
       _error(Format('Found %d accounts with names but %d on list',[LOrdered.Count,FPCAbstractMem.AccountsNames.Count]));
     end;
     end;

+ 25 - 48
src/core/UPCAbstractMemAccountKeys.pas

@@ -10,7 +10,7 @@ uses Classes, SysUtils,
   SyncObjs,
   SyncObjs,
   UAbstractMem, UFileMem, UAbstractMemTList,
   UAbstractMem, UFileMem, UAbstractMemTList,
   UAbstractBTree, UAbstractAVLTree,
   UAbstractBTree, UAbstractAVLTree,
-  UPCDataTypes, UBaseTypes, UAVLCache,
+  UPCDataTypes, UBaseTypes, UAVLCache, UAbstractMemBTree,
   {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults{$ELSE}Generics.Collections,Generics.Defaults{$ENDIF};
   {$IFNDEF FPC}System.Generics.Collections,System.Generics.Defaults{$ELSE}Generics.Collections,Generics.Defaults{$ENDIF};
 
 
 type
 type
@@ -31,16 +31,16 @@ type
 
 
   { TAccountsUsingThisKey }
   { TAccountsUsingThisKey }
 
 
-  TAccountsUsingThisKey = Class(TAbstractMemOrderedTList<Cardinal>)
+  TAccountsUsingThisKey = Class(TAbstractMemBTree)
+    // AbstractMem position will be considered a Account Number
   protected
   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
   public
     Constructor Create(AAbstractMem : TAbstractMem; const AInitialZone : TAMZone; AUseCache : Boolean); reintroduce;
     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;
   End;
 
 
   TAccountKeyByPosition = record
   TAccountKeyByPosition = record
@@ -269,8 +269,10 @@ begin
   try
   try
   Lautk := GetAccountsUsingThisKey(AAccountKey);
   Lautk := GetAccountsUsingThisKey(AAccountKey);
   if Assigned(Lautk) then begin
   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;
   end;
   end;
   finally
   finally
@@ -414,7 +416,6 @@ function TPCAbstractMemAccountKeys.GetPositionOfKeyAndRemoveAccount(
   const AAccountNumber: Cardinal): TAbstractMemPosition;
   const AAccountNumber: Cardinal): TAbstractMemPosition;
 var LNode : TAbstractMemAccountKeyNode;
 var LNode : TAbstractMemAccountKeyNode;
   LZone : TAMZone;
   LZone : TAMZone;
-  i : Integer;
   Lacckutk : TAccountsUsingThisKey;
   Lacckutk : TAccountsUsingThisKey;
   LAccKeyByPos : TAccountKeyByPosition;
   LAccKeyByPos : TAccountKeyByPosition;
 begin
 begin
@@ -445,10 +446,7 @@ begin
   end;
   end;
 
 
   if Assigned(LAccKeyByPos.accountsUsingThisKey) then begin
   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;
   end;
   finally
   finally
     FAccountKeysLock.Release;
     FAccountKeysLock.Release;
@@ -524,53 +522,32 @@ end;
 
 
 { TAccountsUsingThisKey }
 { 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
 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;
 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
 begin
-  Result := ALeft - ARight;
+  // NOTE: Nothing to do NEITHER to inherit from ancestor
 end;
 end;
 
 
-constructor TAccountsUsingThisKey.Create(AAbstractMem: TAbstractMem; const AInitialZone: TAMZone; AUseCache : Boolean);
+function TAccountsUsingThisKey.DoCompareData(const ALeftData, ARightData: TAbstractMemPosition): Integer;
 begin
 begin
-  inherited Create(AAbstractMem,AInitialZone,1000,False, AUseCache);
+  Result := ALeftData - ARightData;
 end;
 end;
 
 
-procedure TAccountsUsingThisKey.Delete(index: Integer);
+function TAccountsUsingThisKey.Get(Index: Integer): Cardinal;
+var i : Integer;
 begin
 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;
 end;
 
 
-function TAccountsUsingThisKey.GetItem(index: Integer): Cardinal;
+function TAccountsUsingThisKey.NodeDataToString(const AData: TAbstractMemPosition): String;
 begin
 begin
-  Result := FList.Position[index];
+  Result := IntToStr(AData);
 end;
 end;
 
 
 { TPCAccountKeyByPositionCache }
 { 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 Disable;
     Procedure Enable;
     Procedure Enable;
     Function ToArray : TCardinalsArray;
     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;
   End;
 
 
 
 
@@ -180,6 +182,36 @@ begin
   if (FDisabledsCount=0) And (FModifiedWhileDisabled) then NotifyChanged;
   if (FDisabledsCount=0) And (FModifiedWhileDisabled) then NotifyChanged;
 end;
 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;
 function TOrderedCardinalList.Find(const Value: Cardinal; var Index: Integer): Boolean;
 var L, H, I: Integer;
 var L, H, I: Integer;
   C : Int64;
   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;
   LAccPubKey : TAccountKey;
   LOutput : TPCJSONArray;
   LOutput : TPCJSONArray;
   LStartsWith : TOrderedRawList;
   LStartsWith : TOrderedRawList;
+  LAccountsList : TList<Integer>;
 begin
 begin
   // Get Parameters
   // Get Parameters
   Result := False;
   Result := False;
@@ -277,21 +278,32 @@ begin
     end;
     end;
   end else begin
   end else begin
     // Search by type-forSale-balance
     // 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;
+    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;
   end;
   end;
   Result := True;
   Result := True;

+ 105 - 32
src/core/UPCRPCOpData.pas

@@ -27,7 +27,7 @@ interface
 Uses classes, SysUtils,
 Uses classes, SysUtils,
   UJSONFunctions, UAccounts, UBaseTypes, UOpTransaction, UConst, UPCDataTypes,
   UJSONFunctions, UAccounts, UBaseTypes, UOpTransaction, UConst, UPCDataTypes,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
-  URPC, UCrypto, UWallet, UBlockChain, ULog;
+  URPC, UCrypto, UEPasa, UWallet, UBlockChain, ULog;
 
 
 
 
 Type
 Type
@@ -132,7 +132,28 @@ class function TRPCOpData.OpData_FindOpDataOperations(
         end;
         end;
       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;
       LOperation : TPCOperation;
       LOpData : TOpData;
       LOpData : TOpData;
       LOperationResume : TOperationResume;
       LOperationResume : TOperationResume;
@@ -148,6 +169,39 @@ class function TRPCOpData.OpData_FindOpDataOperations(
       Try
       Try
         LList := TList<Cardinal>.Create;
         LList := TList<Cardinal>.Create;
         try
         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;
           LLast_block_number := ABlock_number+1;
           while (LLast_block_number>ABlock_number) And (AAct_depth>0)
           while (LLast_block_number>ABlock_number) And (AAct_depth>0)
             And (ABlock_number >= (ASearchedAccount_number DIV CT_AccountsPerBlock))
             And (ABlock_number >= (ASearchedAccount_number DIV CT_AccountsPerBlock))
@@ -171,22 +225,11 @@ class function TRPCOpData.OpData_FindOpDataOperations(
               if LOperation is TOpData then begin
               if LOperation is TOpData then begin
                 //
                 //
                 LOpData := TOpData( LOperation );
                 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;
               end;
             end; // For LList...
             end; // For LList...
@@ -225,8 +268,12 @@ Var LAccount : TAccount;
 begin
 begin
   Result := False;
   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;
   LSearchedAccount_number := CT_MaxAccount;
   LSearchBySender := (LSender>=0) And (LSender<ASender.Node.Bank.AccountsCount);
   LSearchBySender := (LSender>=0) And (LSender<ASender.Node.Bank.AccountsCount);
   LSearchByTarget := (LTarget>=0) And (LTarget<ASender.Node.Bank.AccountsCount);
   LSearchByTarget := (LTarget>=0) And (LTarget<ASender.Node.Bank.AccountsCount);
@@ -267,7 +314,7 @@ begin
   LStartOperation := AInputParams.AsInteger('start',0);
   LStartOperation := AInputParams.AsInteger('start',0);
   LMaxOperations := AInputParams.AsInteger('max',100);
   LMaxOperations := AInputParams.AsInteger('max',100);
   if AInputParams.IndexOfName('startblock')>=0 then begin
   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;
     LFirst_Block_Is_Unknown := True;
   end else begin
   end else begin
     if not ASender.RPCServer.GetMempoolAccount(LSearchedAccount_number,LAccount) then begin
     if not ASender.RPCServer.GetMempoolAccount(LSearchedAccount_number,LAccount) then begin
@@ -277,14 +324,13 @@ begin
     end;
     end;
     LFirst_Block_Is_Unknown := False;
     LFirst_Block_Is_Unknown := False;
     LStartBlock := LAccount.GetLastUpdatedBlock;
     LStartBlock := LAccount.GetLastUpdatedBlock;
-    if LStartBlock>=ASender.Node.Bank.BlocksCount then Dec(LStartBlock); // If its updated on mempool, don't look the mempool
   end;
   end;
 
 
   LOperationsResumeList := TOperationsResumeList.Create;
   LOperationsResumeList := TOperationsResumeList.Create;
   try
   try
     DoFindFromBlock(LStartBlock,
     DoFindFromBlock(LStartBlock,
       LSearchedAccount_number,
       LSearchedAccount_number,
-      LStartOperation, LStartOperation + LMaxOperations,
+      LStartOperation, LStartOperation + LMaxOperations - 1,
       LMaxDepth, LFirst_Block_Is_Unknown,
       LMaxDepth, LFirst_Block_Is_Unknown,
       LSearchBySender, LSender,
       LSearchBySender, LSender,
       LSearchByTarget, LTarget,
       LSearchByTarget, LTarget,
@@ -297,7 +343,9 @@ begin
     LResultArray := AJSONResponse.GetAsArray('result');
     LResultArray := AJSONResponse.GetAsArray('result');
 
 
     for i := 0 to LOperationsResumeList.Count-1 do begin
     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;
     end;
     Result := True;
     Result := True;
   finally
   finally
@@ -313,28 +361,46 @@ var LOpData : TOpData;
   LOperationPayload : TOperationPayload;
   LOperationPayload : TOperationPayload;
   LErrors : String;
   LErrors : String;
   LOPR : TOperationResume;
   LOPR : TOperationResume;
+  LTargetEPASA : TEPasa;
+  LTargetKey : TAccountKey;
+  LTargetRequiresPurchase : Boolean;
 begin
 begin
   Result := False;
   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;
     AErrorNum := CT_RPC_ErrNum_InvalidAccount;
-    AErrorDesc := 'Invalid "sender"';
     Exit;
     Exit;
   end;
   end;
   if (AInputParams.IndexOfName('signer')>=0) then begin
   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;
       AErrorNum := CT_RPC_ErrNum_InvalidAccount;
-      AErrorDesc := 'Invalid "signer"';
       Exit;
       Exit;
     end;
     end;
   end else LSigner := LSender; // If no "signer" param, then use "sender" as signer by default
   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;
     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;
   end;
 
 
   if not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
   if not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
     TCrypto.HexaToRaw(AInputParams.AsString('payload','')),
     TCrypto.HexaToRaw(AInputParams.AsString('payload','')),
+    [ptNonDeterministic],
     AInputParams.AsString('payload_method','none'),
     AInputParams.AsString('payload_method','none'),
     AInputParams.AsString('pwd',''),
     AInputParams.AsString('pwd',''),
     LSender.accountInfo.accountKey,
     LSender.accountInfo.accountKey,
@@ -361,11 +427,17 @@ begin
       Exit;
       Exit;
     end;
     end;
     TPCOperation.OperationToOperationResume(0,LOpData,False,LSender.account,LOPR);
     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;
     Result := True;
   finally
   finally
     LOpData.free;
     LOpData.free;
   end;
   end;
+
+  finally
+    ASender.Node.OperationSequenceLock.Release;
+  end;
 end;
 end;
 
 
 class function TRPCOpData.OpData_SignOpData(const ASender: TRPCProcess;
 class function TRPCOpData.OpData_SignOpData(const ASender: TRPCProcess;
@@ -409,6 +481,7 @@ begin
 
 
     if not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
     if not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
       TCrypto.HexaToRaw(AInputParams.AsString('payload','')),
       TCrypto.HexaToRaw(AInputParams.AsString('payload','')),
+      [ptNonDeterministic],
       AInputParams.AsString('payload_method','dest'),
       AInputParams.AsString('payload_method','dest'),
       AInputParams.AsString('pwd',''),
       AInputParams.AsString('pwd',''),
       LPayloadPubkey,
       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}
 {$ENDIF}
 
 
 Uses Classes, UThread, UAccounts, UBlockChain, UNetProtocol, SysUtils, UNode,
 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};
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 
 type
 type
@@ -42,9 +42,9 @@ type
     FNode : TNode;
     FNode : TNode;
     FNetData : TNetData;
     FNetData : TNetData;
     FWalletKeys : TWalletKeysExt;
     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);
     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);
     procedure DoGiveMeMoney(ANetData : TNetData; ASenderConnection : TNetConnection; const AHeaderData : TNetHeaderData; AReceivedData : TStream; AResponseData : TStream);
     {$ENDIF}
     {$ENDIF}
     procedure OnTNetDataProcessReservedAreaMessage(ANetData : TNetData; ASenderConnection : TNetConnection; const AHeaderData : TNetHeaderData; AReceivedData : TStream; AResponseData : TStream);
     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;
     class function InitNetDataExtraMessages(ANode : TNode; ANetData : TNetData; AWalletKeys : TWalletKeysExt) : TPCTNetDataExtraMessages;
     constructor Create(ANode : TNode; ANetData : TNetData; AWalletKeys : TWalletKeysExt);
     constructor Create(ANode : TNode; ANetData : TNetData; AWalletKeys : TWalletKeysExt);
     destructor Destroy; override;
     destructor Destroy; override;
-    class function AskForFreeAccount(ANewPubliKey : TAccountKey) : Integer;
+    class function AskForFreeAccount(const ANewPubliKey : TAccountKey; const AMessage : String): Integer;
   End;
   End;
 
 
 const
 const
@@ -63,16 +63,16 @@ const
 
 
 implementation
 implementation
 
 
-Uses UOpTransaction, UBaseTypes, ULog;
+Uses UOpTransaction, UBaseTypes, ULog, UPCAbstractMemAccountKeys;
 
 
 var _PCTNetDataExtraMessages : TPCTNetDataExtraMessages = Nil;
 var _PCTNetDataExtraMessages : TPCTNetDataExtraMessages = Nil;
 
 
 { TPCTNetDataExtraMessages }
 { TPCTNetDataExtraMessages }
 
 
-class function TPCTNetDataExtraMessages.AskForFreeAccount(ANewPubliKey : TAccountKey): Integer;
+class function TPCTNetDataExtraMessages.AskForFreeAccount(const ANewPubliKey : TAccountKey; const AMessage : String): Integer;
 begin
 begin
   if Assigned(_PCTNetDataExtraMessages) then begin
   if Assigned(_PCTNetDataExtraMessages) then begin
-    Result := _PCTNetDataExtraMessages.DoAskForFreeAccount(ANewPubliKey);
+    Result := _PCTNetDataExtraMessages.DoAskForFreeAccount(ANewPubliKey,AMessage);
   end else Result := 0;
   end else Result := 0;
 end;
 end;
 
 
@@ -98,15 +98,18 @@ begin
   inherited;
   inherited;
 end;
 end;
 
 
-function TPCTNetDataExtraMessages.DoAskForFreeAccount(ANewPubliKey : TAccountKey): Integer;
+function TPCTNetDataExtraMessages.DoAskForFreeAccount(const ANewPubliKey : TAccountKey; const AMessage : String): Integer;
 var i : Integer;
 var i : Integer;
   LNetConnection : TNetConnection;
   LNetConnection : TNetConnection;
   LRequestStream : TMemoryStream;
   LRequestStream : TMemoryStream;
+  Lraw : TRawBytes;
 begin
 begin
   Result := 0;
   Result := 0;
   LRequestStream := TMemoryStream.Create;
   LRequestStream := TMemoryStream.Create;
   try
   try
     TStreamOp.WriteAccountKey(LRequestStream,ANewPubliKey);
     TStreamOp.WriteAccountKey(LRequestStream,ANewPubliKey);
+    Lraw.FromString(AMessage);
+    TStreamOp.WriteAnsiString(LRequestStream,Lraw);
     LRequestStream.position := 0;
     LRequestStream.position := 0;
     for i := 0 to FNetData.ConnectionsCountAll-1 do begin
     for i := 0 to FNetData.ConnectionsCountAll-1 do begin
       LNetConnection := FNetData.Connection(i);
       LNetConnection := FNetData.Connection(i);
@@ -126,42 +129,76 @@ begin
   end;
   end;
 end;
 end;
 
 
-{$IFDEF TESTNET}
 procedure TPCTNetDataExtraMessages.DoGiveMeAnAccount(ANetData: TNetData;
 procedure TPCTNetDataExtraMessages.DoGiveMeAnAccount(ANetData: TNetData;
   ASenderConnection: TNetConnection; const AHeaderData: TNetHeaderData;
   ASenderConnection: TNetConnection; const AHeaderData: TNetHeaderData;
   AReceivedData, AResponseData: TStream);
   AReceivedData, AResponseData: TStream);
 var LSenderPublicKey : TAccountKey;
 var LSenderPublicKey : TAccountKey;
-  LIndexKey : Integer;
+  LIndexKey,LOnSafebox,LOnMempool : Integer;
   LAccount : TAccount;
   LAccount : TAccount;
-  LOpChangeKey : TOpChangeKey;
+  LOpRecoverFounds : TOpRecoverFounds;
   LPayload : TOperationPayload;
   LPayload : TOperationPayload;
-  LErrors : String;
+  LErrors, LSenderMessage : String;
   LWord : Word;
   LWord : Word;
+  LAccOrd : TAccountsOrderedByUpdatedBlock;
+  LRaw : TRawBytes;
 begin
 begin
   if Not (AHeaderData.header_type in [ntp_request,ntp_autosend]) then Exit; // Nothing to do
   if Not (AHeaderData.header_type in [ntp_request,ntp_autosend]) then Exit; // Nothing to do
   // Protection to allow spam
   // 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;
     TArray<TLimitLifetime>.Create(TLimitLifetime.Create(300,2,20000))) then Exit;
   // Read info
   // Read info
   if TStreamOp.ReadAccountKey(AReceivedData,LSenderPublicKey)<=0 then Exit;
   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
   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
   finally
-    LOpChangeKey.Free;
+    LOpRecoverFounds.Free;
   end;
   end;
-  // Response
-  TStreamOp.WriteAccountKey(AResponseData,LSenderPublicKey);
-  LWord := 1;
-  AResponseData.Write(LWord,SizeOf(LWord));
-  AResponseData.Write(LAccount.account,SizeOf(LAccount.account));
 end;
 end;
 
 
+{$IFDEF TESTNET}
 procedure TPCTNetDataExtraMessages.DoGiveMeMoney(ANetData: TNetData;
 procedure TPCTNetDataExtraMessages.DoGiveMeMoney(ANetData: TNetData;
   ASenderConnection: TNetConnection; const AHeaderData: TNetHeaderData;
   ASenderConnection: TNetConnection; const AHeaderData: TNetHeaderData;
   AReceivedData, AResponseData: TStream);
   AReceivedData, AResponseData: TStream);
@@ -207,6 +244,7 @@ class function TPCTNetDataExtraMessages.InitNetDataExtraMessages(ANode: TNode;
   ANetData: TNetData; AWalletKeys: TWalletKeysExt): TPCTNetDataExtraMessages;
   ANetData: TNetData; AWalletKeys: TWalletKeysExt): TPCTNetDataExtraMessages;
 begin
 begin
   if not Assigned(_PCTNetDataExtraMessages) then begin
   if not Assigned(_PCTNetDataExtraMessages) then begin
+    TLog.NewLog(ltinfo,ClassName,'InitNetDataExtraMessages');
     _PCTNetDataExtraMessages := TPCTNetDataExtraMessages.Create(ANode,ANetData,AWalletKeys);
     _PCTNetDataExtraMessages := TPCTNetDataExtraMessages.Create(ANode,ANetData,AWalletKeys);
   end;
   end;
   Result := _PCTNetDataExtraMessages;
   Result := _PCTNetDataExtraMessages;
@@ -215,12 +253,12 @@ end;
 procedure TPCTNetDataExtraMessages.OnTNetDataProcessReservedAreaMessage(ANetData : TNetData; ASenderConnection : TNetConnection; const AHeaderData : TNetHeaderData; AReceivedData : TStream; AResponseData : TStream);
 procedure TPCTNetDataExtraMessages.OnTNetDataProcessReservedAreaMessage(ANetData : TNetData; ASenderConnection : TNetConnection; const AHeaderData : TNetHeaderData; AReceivedData : TStream; AResponseData : TStream);
 begin
 begin
   TLog.NewLog(ltdebug,ClassName,Format('Received extra message from %s Operation:%d',[ASenderConnection.ClientRemoteAddr,AHeaderData.operation]));
   TLog.NewLog(ltdebug,ClassName,Format('Received extra message from %s Operation:%d',[ASenderConnection.ClientRemoteAddr,AHeaderData.operation]));
-  {$IFDEF TESTNET}
   case AHeaderData.operation of
   case AHeaderData.operation of
     CT_NetProtocol_Extra_NetOp_GIVE_ME_AN_ACCOUNT : DoGiveMeAnAccount(ANetData,ASenderConnection,AHeaderData,AReceivedData,AResponseData);
     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);
     CT_NetProtocol_Extra_NetOp_GIVE_ME_MONEY : DoGiveMeMoney(ANetData,ASenderConnection,AHeaderData,AReceivedData,AResponseData);
+    {$ENDIF}
   end;
   end;
-  {$ENDIF}
 end;
 end;
 
 
 function TPCTNetDataExtraMessages.RandomGetWalletKeysAccount(
 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;
     WorkingMillisecondsTotal : Cardinal;
     WinsCount : Integer;
     WinsCount : Integer;
     Invalids : Integer;
     Invalids : Integer;
+    InternalComputingRounds : Integer;
   End;
   End;
 
 
 Const
 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
 Type
 
 
@@ -626,11 +627,6 @@ begin
 end;
 end;
 
 
 procedure TCustomMinerDeviceThread.UpdateDeviceStats(Stats: TMinerStats);
 procedure TCustomMinerDeviceThread.UpdateDeviceStats(Stats: TMinerStats);
-Type TTimeMinerStats = Record
-       tc : Cardinal;
-       stats : TMinerStats;
-     end;
-  PTimeMinerStats = ^TTimeMinerStats;
 Var l : TList<Pointer>;
 Var l : TList<Pointer>;
   i : Integer;
   i : Integer;
   P : PTimeMinerStats;
   P : PTimeMinerStats;
@@ -657,6 +653,7 @@ begin
         if ((stats.Miners>foundMaxMiners)) then foundMaxMiners := stats.Miners;
         if ((stats.Miners>foundMaxMiners)) then foundMaxMiners := stats.Miners;
       end;
       end;
     end;
     end;
+    FPartialDeviceStats.InternalComputingRounds:=l.count;
     If l.count>0 then begin
     If l.count>0 then begin
       P := PTimeMinerStats(l[l.count-1]);
       P := PTimeMinerStats(l[l.count-1]);
       FPartialDeviceStats.WorkingMillisecondsHashing:=P^.tc - PTimeMinerStats(l[0]).tc + P^.stats.WorkingMillisecondsHashing;
       FPartialDeviceStats.WorkingMillisecondsHashing:=P^.tc - PTimeMinerStats(l[0]).tc + P^.stats.WorkingMillisecondsHashing;
@@ -863,7 +860,7 @@ begin
             if FCurrentMinerValuesForWork.version < CT_PROTOCOL_5 then
             if FCurrentMinerValuesForWork.version < CT_PROTOCOL_5 then
               roundsToDo := 20
               roundsToDo := 20
             else
             else
-              roundsToDo := 200+Random(200);
+              roundsToDo := 100+Random(100);
           end else begin
           end else begin
             roundsToDo := 10000;
             roundsToDo := 10000;
           end;
           end;
@@ -982,6 +979,7 @@ begin
               finalHashingTC:=TPlatform.GetTickCount;
               finalHashingTC:=TPlatform.GetTickCount;
             end;
             end;
             AuxStats.Miners:=FCPUDeviceThread.FCPUs;
             AuxStats.Miners:=FCPUDeviceThread.FCPUs;
+            AuxStats.InternalComputingRounds:=roundsToDo;
             AuxStats.RoundsCount:=LRoundsPerformed;
             AuxStats.RoundsCount:=LRoundsPerformed;
             AuxStats.WorkingMillisecondsTotal:=TPlatform.GetTickCount - baseRealTC;
             AuxStats.WorkingMillisecondsTotal:=TPlatform.GetTickCount - baseRealTC;
             AuxStats.WorkingMillisecondsHashing:= finalHashingTC - baseHashingTC;
             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_SaveDebugLogs = 'SaveDebugLogs';
   CT_PARAM_ShowLogs = 'ShowLogs';
   CT_PARAM_ShowLogs = 'ShowLogs';
   CT_PARAM_MinerName = 'MinerName';
   CT_PARAM_MinerName = 'MinerName';
+  CT_PARAM_MaxPayToKeyPurchasePrice = 'MaxPayToKeyPurchasePrice';
   CT_PARAM_RunCount = 'RunCount';
   CT_PARAM_RunCount = 'RunCount';
   CT_PARAM_FirstTime = 'FirstTime';
   CT_PARAM_FirstTime = 'FirstTime';
   CT_PARAM_ShowModalMessages = 'ShowModalMessages';
   CT_PARAM_ShowModalMessages = 'ShowModalMessages';
@@ -61,6 +62,10 @@ type
 
 
   TMinerPrivateKeyType = (mpk_NewEachTime, mpk_Random, mpk_Selected);
   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 }
 
 
   TSettings = class
   TSettings = class
@@ -72,19 +77,15 @@ type
       class function GetMinFutureBlocksToDownloadNewSafebox: Integer; static;
       class function GetMinFutureBlocksToDownloadNewSafebox: Integer; static;
       class procedure SetAllowDownloadNewCheckpointIfOlderThan(ABool: Boolean); static;
       class procedure SetAllowDownloadNewCheckpointIfOlderThan(ABool: Boolean); static;
       class procedure SetInternetServerPort(AInt:Integer); 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 SetMinFutureBlocksToDownloadNewSafebox(AInt: Integer); static;
-      class procedure SetRpcPortEnabled(ABool: boolean); static;
+      class procedure SetJsonRpcPortEnabled(ABool: boolean); static;
       class function GetDefaultFee : Int64; static;
       class function GetDefaultFee : Int64; static;
       class procedure SetDefaultFee(AInt64: Int64); static;
       class procedure SetDefaultFee(AInt64: Int64); static;
       class function GetMinerPrivateKeyType : TMinerPrivateKeyType; static;
       class function GetMinerPrivateKeyType : TMinerPrivateKeyType; static;
       class procedure SetMinerPrivateKeyType(AType: 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 function GetSaveLogFiles : boolean; static;
       class procedure SetSaveLogFiles(ABool: boolean); static;
       class procedure SetSaveLogFiles(ABool: boolean); static;
       class function GetShowLogs : boolean; static;
       class function GetShowLogs : boolean; static;
@@ -95,14 +96,27 @@ type
       class procedure SetMinerName(AName: string); static;
       class procedure SetMinerName(AName: string); static;
       class function GetRunCount : Integer; static;
       class function GetRunCount : Integer; static;
       class procedure SetRunCount(AInt: 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 function GetShowModalMessages : boolean; static;
       class procedure SetShowModalMessages(ABool: 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 function GetPeerCache : string; static;
       class procedure SetPeerCache(AString: string); static;
       class procedure SetPeerCache(AString: string); static;
       class function GetTryConnectOnlyWithThisFixedServers : string; static;
       class function GetTryConnectOnlyWithThisFixedServers : string; static;
       class procedure SetTryConnectOnlyWithThisFixedServers(AString: string); static;
       class procedure SetTryConnectOnlyWithThisFixedServers(AString: string); static;
+      class procedure CheckNotLoaded;
       class procedure CheckLoaded;
       class procedure CheckLoaded;
       class procedure NotifyOnChanged;
       class procedure NotifyOnChanged;
     public
     public
@@ -110,18 +124,22 @@ type
       class procedure Save;
       class procedure Save;
       class property OnChanged : TNotifyManyEvent read FOnChanged;
       class property OnChanged : TNotifyManyEvent read FOnChanged;
       class property InternetServerPort : Integer read GetInternetServerPort write SetInternetServerPort;
       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 DefaultFee : Int64 read GetDefaultFee write SetDefaultFee;
       class property MinerPrivateKeyType : TMinerPrivateKeyType read GetMinerPrivateKeyType write SetMinerPrivateKeyType;
       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 SaveLogFiles : boolean read GetSaveLogFiles write SetSaveLogFiles;
       class property ShowLogs : boolean read GetShowLogs write SetShowLogs;
       class property ShowLogs : boolean read GetShowLogs write SetShowLogs;
       class property SaveDebugLogs : boolean read GetSaveDebugLogs write SetSaveDebugLogs;
       class property SaveDebugLogs : boolean read GetSaveDebugLogs write SetSaveDebugLogs;
       class property MinerName : string read GetMinerName write SetMinerName;
       class property MinerName : string read GetMinerName write SetMinerName;
       class property RunCount : Integer read GetRunCount write SetRunCount;
       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 ShowModalMessages : boolean read GetShowModalMessages write SetShowModalMessages;
       class property PeerCache : string read GetPeerCache write SetPeerCache;
       class property PeerCache : string read GetPeerCache write SetPeerCache;
       class property TryConnectOnlyWithThisFixedServers : string read GetTryConnectOnlyWithThisFixedServers write SetTryConnectOnlyWithThisFixedServers;
       class property TryConnectOnlyWithThisFixedServers : string read GetTryConnectOnlyWithThisFixedServers write SetTryConnectOnlyWithThisFixedServers;
@@ -140,13 +158,22 @@ uses
 
 
 class procedure TSettings.Load;
 class procedure TSettings.Load;
 begin
 begin
+  CheckNotLoaded;
   FAppParams := TAppParams.Create(nil);
   FAppParams := TAppParams.Create(nil);
   FAppParams.FileName := TNode.GetPascalCoinDataFolder+PathDelim+'AppParams.prm';
   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;
 end;
 
 
 class procedure TSettings.Save;
 class procedure TSettings.Save;
 begin
 begin
   //TODO Update FAppParams to optionally save on set value, and make FApp.Save public and verify all AppParams updates in client code
   //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;
 end;
 
 
 class function TSettings.GetInternetServerPort : Integer;
 class function TSettings.GetInternetServerPort : Integer;
@@ -179,36 +206,60 @@ begin
   FAppParams.ParamByName[CT_PARAM_InternetServerPort].SetAsInteger(AInt);
   FAppParams.ParamByName[CT_PARAM_InternetServerPort].SetAsInteger(AInt);
 end;
 end;
 
 
-class function TSettings.GetRpcPortEnabled : boolean;
+class procedure TSettings.SetMinFutureBlocksToDownloadNewSafebox(AInt: Integer);
 begin
 begin
   CheckLoaded;
   CheckLoaded;
-  Result := FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].GetAsBoolean(false);
+  FAppParams.ParamByName[CT_PARAM_MinFutureBlocksToDownloadNewSafebox].SetAsInteger(AInt);
 end;
 end;
 
 
-class procedure TSettings.SetMinFutureBlocksToDownloadNewSafebox(AInt: Integer);
+class function TSettings.GetJsonRpcPortEnabled : boolean;
 begin
 begin
   CheckLoaded;
   CheckLoaded;
-  FAppParams.ParamByName[CT_PARAM_MinFutureBlocksToDownloadNewSafebox].SetAsInteger(AInt);
+  Result := FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].GetAsBoolean(false);
 end;
 end;
 
 
-class procedure TSettings.SetRpcPortEnabled(ABool: boolean);
+class procedure TSettings.SetJsonRpcPortEnabled(ABool: boolean);
 begin
 begin
   CheckLoaded;
   CheckLoaded;
   FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].SetAsBoolean(ABool);
   FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].SetAsBoolean(ABool);
 end;
 end;
 
 
-class function TSettings.GetRpcAllowedIPs : string;
+class function TSettings.GetJsonRpcAllowedIPs : string;
 begin
 begin
   CheckLoaded;
   CheckLoaded;
   Result := FAppParams.ParamByName[CT_PARAM_JSONRPCAllowedIPs].GetAsString('127.0.0.1;');
   Result := FAppParams.ParamByName[CT_PARAM_JSONRPCAllowedIPs].GetAsString('127.0.0.1;');
 end;
 end;
 
 
-class procedure TSettings.SetRpcAllowedIPs(AString: string);
+class procedure TSettings.SetJsonRpcAllowedIPs(AString: string);
 begin
 begin
   CheckLoaded;
   CheckLoaded;
   FAppParams.ParamByName[CT_PARAM_JSONRPCAllowedIPs].SetAsString(AString);
   FAppParams.ParamByName[CT_PARAM_JSONRPCAllowedIPs].SetAsString(AString);
 end;
 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;
 class function TSettings.GetDefaultFee : Int64;
 begin
 begin
   CheckLoaded;
   CheckLoaded;
@@ -221,13 +272,13 @@ begin
   FAppParams.ParamByName[CT_PARAM_DefaultFee].SetAsInt64(AInt64);
   FAppParams.ParamByName[CT_PARAM_DefaultFee].SetAsInt64(AInt64);
 end;
 end;
 
 
-class function TSettings.GetMinerServerRpcActive : boolean;
+class function TSettings.GetJsonRpcMinerServerActive : boolean;
 begin
 begin
   CheckLoaded;
   CheckLoaded;
   Result := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].GetAsBoolean(true);
   Result := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].GetAsBoolean(true);
 end;
 end;
 
 
-class procedure TSettings.SetMinerServerRpcActive(ABool: Boolean);
+class procedure TSettings.SetJsonRpcMinerServerActive(ABool: Boolean);
 begin
 begin
   CheckLoaded;
   CheckLoaded;
   FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].SetAsBoolean(ABool);
   FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].SetAsBoolean(ABool);
@@ -257,13 +308,13 @@ begin
   FAppParams.ParamByName[CT_PARAM_MinerPrivateKeyType].SetAsInteger(Integer(AType));
   FAppParams.ParamByName[CT_PARAM_MinerPrivateKeyType].SetAsInteger(Integer(AType));
 end;
 end;
 
 
-class function TSettings.GetMinerSelectedPrivateKey : TRawBytes;
+class function TSettings.GetMinerSelectedPublicKey : TRawBytes;
 begin
 begin
   CheckLoaded;
   CheckLoaded;
   Result := FAppParams.ParamByName[CT_PARAM_MinerPrivateKeySelectedPublicKey].GetAsTBytes(Nil);
   Result := FAppParams.ParamByName[CT_PARAM_MinerPrivateKeySelectedPublicKey].GetAsTBytes(Nil);
 end;
 end;
 
 
-class procedure TSettings.SetMinerSelectedPrivateKey(AKey:TRawBytes);
+class procedure TSettings.SetMinerSelectedPublicKey(AKey:TRawBytes);
 begin
 begin
   CheckLoaded;
   CheckLoaded;
   FAppParams.ParamByName[CT_PARAM_MinerPrivateKeySelectedPublicKey].SetAsTBytes(AKey);
   FAppParams.ParamByName[CT_PARAM_MinerPrivateKeySelectedPublicKey].SetAsTBytes(AKey);
@@ -329,6 +380,30 @@ begin
   FAppParams.ParamByName[CT_PARAM_RunCount].SetAsInteger(AInt)
   FAppParams.ParamByName[CT_PARAM_RunCount].SetAsInteger(AInt)
 end;
 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;
 class function TSettings.GetShowModalMessages : boolean;
 begin
 begin
   CheckLoaded;
   CheckLoaded;
@@ -365,6 +440,12 @@ begin
   FAppParams.ParamByName[CT_PARAM_TryToConnectOnlyWithThisFixedServers].SetAsString(Trim(AString));
   FAppParams.ParamByName[CT_PARAM_TryToConnectOnlyWithThisFixedServers].SetAsString(Trim(AString));
 end;
 end;
 
 
+class procedure TSettings.CheckNotLoaded;
+begin
+  if Assigned(FAppParams) then
+    raise Exception.Create('Application settings have already been loaded');
+end;
+
 class procedure TSettings.CheckLoaded;
 class procedure TSettings.CheckLoaded;
 begin
 begin
   if not Assigned(FAppParams) then
   if not Assigned(FAppParams) then

+ 6 - 1
src/core/UTCPIP.pas

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

+ 1 - 1
src/core/UTxMultiOperation.pas

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

+ 2 - 2
src/core/UWallet.pas

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

+ 3 - 5
src/core/upcdaemon.pas

@@ -48,6 +48,7 @@ Const
   CT_INI_IDENT_MINPENDINGBLOCKSTODOWNLOADCHECKPOINT = 'MINPENDINGBLOCKSTODOWNLOADCHECKPOINT';
   CT_INI_IDENT_MINPENDINGBLOCKSTODOWNLOADCHECKPOINT = 'MINPENDINGBLOCKSTODOWNLOADCHECKPOINT';
   CT_INI_IDENT_PEERCACHE = 'PEERCACHE';
   CT_INI_IDENT_PEERCACHE = 'PEERCACHE';
   CT_INI_IDENT_DATA_FOLDER = 'DATAFOLDER';
   CT_INI_IDENT_DATA_FOLDER = 'DATAFOLDER';
+  CT_INI_IDENT_NODE_MAX_PAYTOKEY_MOLINAS = 'MAX_PAYTOKEY_MOLINAS';
   {$IFDEF USE_ABSTRACTMEM}
   {$IFDEF USE_ABSTRACTMEM}
   CT_INI_IDENT_ABSTRACTMEM_MAX_CACHE_MB = 'ABSTRACTMEM_MAX_CACHE_MB';
   CT_INI_IDENT_ABSTRACTMEM_MAX_CACHE_MB = 'ABSTRACTMEM_MAX_CACHE_MB';
   CT_INI_IDENT_ABSTRACTMEM_USE_CACHE_ON_LISTS = 'ABSTRACTMEM_USE_CACHE_ON_LISTS';
   CT_INI_IDENT_ABSTRACTMEM_USE_CACHE_ON_LISTS = 'ABSTRACTMEM_USE_CACHE_ON_LISTS';
@@ -105,9 +106,7 @@ Type
 
 
 implementation
 implementation
 
 
-{$IFDEF TESTNET}
 uses UPCTNetDataExtraMessages;
 uses UPCTNetDataExtraMessages;
-{$ENDIF}
 
 
 Var _FLog : TLog;
 Var _FLog : TLog;
 
 
@@ -276,9 +275,6 @@ begin
       FNode.Bank.SafeBox.PCAbstractMem.MaxAccountsCache := LCacheMaxAccounts;
       FNode.Bank.SafeBox.PCAbstractMem.MaxAccountsCache := LCacheMaxAccounts;
       FNode.Bank.SafeBox.PCAbstractMem.MaxAccountKeysCache := LCacheMaxPubKeys;
       FNode.Bank.SafeBox.PCAbstractMem.MaxAccountKeysCache := LCacheMaxPubKeys;
       {$ENDIF}
       {$ENDIF}
-      {$IFDEF TESTNET}
-      TPCTNetDataExtraMessages.InitNetDataExtraMessages(FNode,TNetData.NetData,FWalletKeys);
-      {$ENDIF}
       // RPC Server
       // RPC Server
       InitRPCServer;
       InitRPCServer;
       Try
       Try
@@ -295,12 +291,14 @@ begin
         FWalletKeys.SafeBox := FNode.Node.Bank.SafeBox;
         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.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.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.AutoDiscoverNodes(CT_Discover_IPs);
         FNode.Node.NetServer.Active := true;
         FNode.Node.NetServer.Active := true;
 
 
         // RPC Miner Server
         // RPC Miner Server
         InitRPCMinerServer;
         InitRPCMinerServer;
         Try
         Try
+          TPCTNetDataExtraMessages.InitNetDataExtraMessages(FNode,TNetData.NetData,FWalletKeys);
           Repeat
           Repeat
             Sleep(100);
             Sleep(100);
           Until (Terminated) or (Application.Terminated);
           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
   object bbCancel: TBitBtn
     Left = 461
     Left = 461
     Top = 476
     Top = 476
-    Width = 116
+    Width = 120
     Height = 31
     Height = 31
     Kind = bkCancel
     Kind = bkCancel
     NumGlyphs = 2
     NumGlyphs = 2
@@ -185,7 +185,7 @@ object FRMOperation: TFRMOperation
         object lblEncryptionErrors: TLabel
         object lblEncryptionErrors: TLabel
           Left = 255
           Left = 255
           Top = 96
           Top = 96
-          Width = 187
+          Width = 152
           Height = 33
           Height = 33
           AutoSize = False
           AutoSize = False
           Caption = 'Errors detected'
           Caption = 'Errors detected'
@@ -211,7 +211,7 @@ object FRMOperation: TFRMOperation
         object rbEncryptedWithEC: TRadioButton
         object rbEncryptedWithEC: TRadioButton
           Left = 15
           Left = 15
           Top = 35
           Top = 35
-          Width = 207
+          Width = 234
           Height = 19
           Height = 19
           Caption = 'Encrypted with dest account public key'
           Caption = 'Encrypted with dest account public key'
           Checked = True
           Checked = True
@@ -222,7 +222,7 @@ object FRMOperation: TFRMOperation
         object rbEncrptedWithPassword: TRadioButton
         object rbEncrptedWithPassword: TRadioButton
           Left = 15
           Left = 15
           Top = 53
           Top = 53
-          Width = 141
+          Width = 234
           Height = 19
           Height = 19
           Caption = 'Encrypted with password'
           Caption = 'Encrypted with password'
           TabOrder = 2
           TabOrder = 2
@@ -232,7 +232,7 @@ object FRMOperation: TFRMOperation
         object rbNotEncrypted: TRadioButton
         object rbNotEncrypted: TRadioButton
           Left = 15
           Left = 15
           Top = 93
           Top = 93
-          Width = 162
+          Width = 234
           Height = 19
           Height = 19
           Caption = 'Dont encrypt (Public payload)'
           Caption = 'Dont encrypt (Public payload)'
           TabOrder = 4
           TabOrder = 4
@@ -266,21 +266,25 @@ object FRMOperation: TFRMOperation
         end
         end
         object rbEncryptedWithOldEC: TRadioButton
         object rbEncryptedWithOldEC: TRadioButton
           Left = 15
           Left = 15
-          Top = 16
-          Width = 159
+          Top = 12
+          Width = 234
           Height = 19
           Height = 19
           Caption = 'Encrypted with old public key'
           Caption = 'Encrypted with old public key'
           TabOrder = 0
           TabOrder = 0
           TabStop = True
           TabStop = True
           OnClick = memoPayloadClick
           OnClick = memoPayloadClick
         end
         end
-        object cbPayloadAsHex: TCheckBox
-          Left = 448
+        object cbPayloadDataInputType: TComboBox
+          Left = 413
           Top = 96
           Top = 96
-          Width = 97
-          Height = 17
-          Caption = 'As Hex'
+          Width = 89
+          Height = 21
+          Style = csDropDownList
           TabOrder = 6
           TabOrder = 6
+          Items.Strings = (
+            'As String'#11
+            'As Hexadecimal'#11
+            'As Base58')
         end
         end
       end
       end
       object ebFee: TEdit
       object ebFee: TEdit
@@ -295,11 +299,14 @@ object FRMOperation: TFRMOperation
         Top = 7
         Top = 7
         Width = 524
         Width = 524
         Height = 204
         Height = 204
-        ActivePage = tsBuyAccount
+        ActivePage = tsTransaction
         TabOrder = 0
         TabOrder = 0
         OnChange = PageControlOpTypeChange
         OnChange = PageControlOpTypeChange
         object tsTransaction: TTabSheet
         object tsTransaction: TTabSheet
           Caption = 'Transaction'
           Caption = 'Transaction'
+          DesignSize = (
+            516
+            176)
           object lblDestAccount: TLabel
           object lblDestAccount: TLabel
             Left = 13
             Left = 13
             Top = 32
             Top = 32
@@ -335,10 +342,11 @@ object FRMOperation: TFRMOperation
             ParentFont = False
             ParentFont = False
           end
           end
           object sbSearchDestinationAccount: TSpeedButton
           object sbSearchDestinationAccount: TSpeedButton
-            Left = 208
+            Left = 490
             Top = 29
             Top = 29
             Width = 23
             Width = 23
             Height = 22
             Height = 22
+            Anchors = [akTop, akRight]
             Glyph.Data = {
             Glyph.Data = {
               36030000424D3603000000000000360000002800000010000000100000000100
               36030000424D3603000000000000360000002800000010000000100000000100
               18000000000000030000120B0000120B00000000000000000000FF00FF4A667C
               18000000000000030000120B0000120B00000000000000000000FF00FF4A667C
@@ -371,17 +379,31 @@ object FRMOperation: TFRMOperation
           object ebDestAccount: TEdit
           object ebDestAccount: TEdit
             Left = 115
             Left = 115
             Top = 29
             Top = 29
-            Width = 87
+            Width = 369
             Height = 21
             Height = 21
+            Anchors = [akLeft, akTop, akRight]
             TabOrder = 0
             TabOrder = 0
           end
           end
           object ebAmount: TEdit
           object ebAmount: TEdit
             Left = 115
             Left = 115
-            Top = 58
+            Top = 56
             Width = 87
             Width = 87
             Height = 21
             Height = 21
             TabOrder = 1
             TabOrder = 1
           end
           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
         end
         object tsChangePrivateKey: TTabSheet
         object tsChangePrivateKey: TTabSheet
           Caption = 'Change Key'
           Caption = 'Change Key'

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

@@ -15,7 +15,7 @@ object FRMOperation: TFRMOperation
   OnCreate = FormCreate
   OnCreate = FormCreate
   OnDestroy = FormDestroy
   OnDestroy = FormDestroy
   Position = poOwnerFormCenter
   Position = poOwnerFormCenter
-  LCLVersion = '2.0.2.0'
+  LCLVersion = '2.0.10.0'
   object lblAccountCaption: TLabel
   object lblAccountCaption: TLabel
     Left = 25
     Left = 25
     Height = 13
     Height = 13
@@ -142,7 +142,7 @@ object FRMOperation: TFRMOperation
           Left = 255
           Left = 255
           Height = 33
           Height = 33
           Top = 87
           Top = 87
-          Width = 182
+          Width = 153
           AutoSize = False
           AutoSize = False
           Caption = 'Errors detected'
           Caption = 'Errors detected'
           Font.Color = clRed
           Font.Color = clRed
@@ -223,12 +223,18 @@ object FRMOperation: TFRMOperation
           OnClick = memoPayloadClick
           OnClick = memoPayloadClick
           TabOrder = 0
           TabOrder = 0
         end
         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
           TabOrder = 6
         end
         end
       end
       end
@@ -244,8 +250,8 @@ object FRMOperation: TFRMOperation
         Height = 167
         Height = 167
         Top = 11
         Top = 11
         Width = 521
         Width = 521
-        ActivePage = tsListAccount
-        TabIndex = 2
+        ActivePage = tsTransaction
+        TabIndex = 0
         TabOrder = 0
         TabOrder = 0
         OnChange = PageControlOpTypeChange
         OnChange = PageControlOpTypeChange
         object tsTransaction: TTabSheet
         object tsTransaction: TTabSheet
@@ -285,7 +291,7 @@ object FRMOperation: TFRMOperation
             Left = 115
             Left = 115
             Height = 21
             Height = 21
             Top = 29
             Top = 29
-            Width = 85
+            Width = 357
             TabOrder = 0
             TabOrder = 0
           end
           end
           object ebAmount: TEdit
           object ebAmount: TEdit
@@ -296,9 +302,9 @@ object FRMOperation: TFRMOperation
             TabOrder = 1
             TabOrder = 1
           end
           end
           object sbSearchDestinationAccount: TSpeedButton
           object sbSearchDestinationAccount: TSpeedButton
-            Left = 208
+            Left = 480
             Height = 22
             Height = 22
-            Top = 29
+            Top = 28
             Width = 23
             Width = 23
             Glyph.Data = {
             Glyph.Data = {
               36030000424D3803000000000000360000002800000010000000100000000100
               36030000424D3803000000000000360000002800000010000000100000000100
@@ -330,6 +336,23 @@ object FRMOperation: TFRMOperation
             }
             }
             OnClick = sbSearchDestinationAccountClick
             OnClick = sbSearchDestinationAccountClick
           end
           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
         end
         object tsChangePrivateKey: TTabSheet
         object tsChangePrivateKey: TTabSheet
           Caption = 'Change key'
           Caption = 'Change key'
@@ -1024,7 +1047,7 @@ object FRMOperation: TFRMOperation
     end
     end
     object tsGlobalError: TTabSheet
     object tsGlobalError: TTabSheet
       Caption = 'Notification'
       Caption = 'Notification'
-      ClientHeight = 325
+      ClientHeight = 357
       ClientWidth = 548
       ClientWidth = 548
       ImageIndex = 1
       ImageIndex = 1
       TabVisible = False
       TabVisible = False
@@ -1148,8 +1171,8 @@ object FRMOperation: TFRMOperation
     Text = 'ebSenderAccount'
     Text = 'ebSenderAccount'
   end
   end
   object ActionList: TActionList
   object ActionList: TActionList
-    left = 140
-    top = 350
+    Left = 140
+    Top = 350
     object actExecute: TAction
     object actExecute: TAction
       Caption = 'Execute (F12)'
       Caption = 'Execute (F12)'
       OnExecute = actExecuteExecute
       OnExecute = actExecuteExecute

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

@@ -32,7 +32,7 @@ uses
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs, StdCtrls, UNode, UWallet, UCrypto, Buttons, UBlockChain,
   Dialogs, StdCtrls, UNode, UWallet, UCrypto, Buttons, UBlockChain,
   UAccounts, UFRMAccountSelect, ActnList, ComCtrls, Types, UFRMMemoText,
   UAccounts, UFRMAccountSelect, ActnList, ComCtrls, Types, UFRMMemoText,
-  UPCEncryption, UBaseTypes, UPCDataTypes, UPCOrderedLists;
+  UPCEncryption, UBaseTypes, UPCDataTypes, UPCOrderedLists, UEPasa, UEncoding;
 
 
 Const
 Const
   CM_PC_WalletKeysChanged = WM_USER + 1;
   CM_PC_WalletKeysChanged = WM_USER + 1;
@@ -129,9 +129,10 @@ type
     ebHashLock: TEdit;
     ebHashLock: TEdit;
     btnHashLock: TSpeedButton;
     btnHashLock: TSpeedButton;
     sbTimeLock: TSpeedButton;
     sbTimeLock: TSpeedButton;
-    cbPayloadAsHex: TCheckBox;
     lblChangeAccountData: TLabel;
     lblChangeAccountData: TLabel;
     ebChangeAccountData: TEdit;
     ebChangeAccountData: TEdit;
+    cbPayloadDataInputType: TComboBox;
+    memoEPASA: TMemo;
     procedure ebNewPublicKeyExit(Sender: TObject);
     procedure ebNewPublicKeyExit(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
@@ -159,6 +160,7 @@ type
     FDefaultFee: Int64;
     FDefaultFee: Int64;
     FEncodedPayload : TOperationPayload;
     FEncodedPayload : TOperationPayload;
     FDisabled : Boolean;
     FDisabled : Boolean;
+    FUpdating : Boolean;
     FSenderAccounts: TOrderedCardinalList; // TODO: TOrderedCardinalList should be replaced with a "TCardinalList" since signer account should be processed last
     FSenderAccounts: TOrderedCardinalList; // TODO: TOrderedCardinalList should be replaced with a "TCardinalList" since signer account should be processed last
     procedure SetWalletKeys(const Value: TWalletKeys);
     procedure SetWalletKeys(const Value: TWalletKeys);
     Procedure UpdateWalletKeys;
     Procedure UpdateWalletKeys;
@@ -166,8 +168,8 @@ type
     Procedure UpdateAccountsInfo;
     Procedure UpdateAccountsInfo;
     Function UpdateFee(var Fee : Int64; errors : String) : Boolean;
     Function UpdateFee(var Fee : Int64; errors : String) : Boolean;
     Function UpdateOperationOptions(var 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 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 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;
     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;
     procedure CM_WalletChanged(var Msg: TMessage); message CM_PC_WalletKeysChanged;
     Function GetDefaultSenderAccount : TAccount;
     Function GetDefaultSenderAccount : TAccount;
     procedure ebAccountKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
     procedure ebAccountKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
+    function CaptureEPasa(const AEPasaTxt : String; out AEPasa : TEPasa) : Boolean;
   protected
   protected
     procedure searchAccount(editBox : TCustomEdit);
     procedure searchAccount(editBox : TCustomEdit);
   public
   public
@@ -203,215 +206,224 @@ uses
 { TFRMOperation }
 { TFRMOperation }
 
 
 procedure TFRMOperation.actExecuteExecute(Sender: TObject);
 procedure TFRMOperation.actExecuteExecute(Sender: TObject);
-Var errors : String;
-  P : PAccount;
-  i,iAcc : Integer;
+var
+  LErrors : String;
+  LAccountPtr : PAccount;
+  i,LAccountNo : Integer;
   LKey : TWalletKey;
   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;
   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;
 label loop_start;
 begin
 begin
   if Not Assigned(WalletKeys) then raise Exception.Create('No wallet keys');
   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
   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
     // Compile FSenderAccounts into a reorderable array
-    _senderAccounts := FSenderAccounts.ToArray;
+    LSenderAccounts := FSenderAccounts.ToArray;
 
 
     // Loop through each sender account
     // 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:
 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  (
         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');
             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 else Raise Exception.Create('Sender account private key not found in Wallet');
       end;
       end;
-      dooperation := true;
+      LDoOperation := true;
       // Default fee
       // 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
       // Determine which operation type it is
       if PageControlOpType.ActivePage = tsTransaction then begin
       if PageControlOpType.ActivePage = tsTransaction then begin
         {%region Operation: Transaction}
         {%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
             end else begin
-              _amount := account.balance;
-              _fee := 0;
+              LAmount := LAccount.balance;
+              LFee := 0;
             end;
             end;
-          end else dooperation := false;
-        end else begin
+          end else LDoOperation := false;
         end;
         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;
         end;
-        operationstxt := 'Transaction to '+TAccountComp.AccountNumberToAccountTxtNumber(destAccount.account);
         {%endregion}
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsChangePrivateKey) then begin
       end else if (PageControlOpType.ActivePage = tsChangePrivateKey) then begin
         {%region Operation: Change Private Key}
         {%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)
           // 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!
             goto loop_start; // TODO: remove ugly hack with refactoring!
           end;
           end;
 
 
           // Maintain correct signer fee distribution
           // 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
         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;
         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}
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsListAccount) then begin
       end else if (PageControlOpType.ActivePage = tsListAccount) then begin
         {%region Operation: List For Sale}
         {%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:
         // 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
         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
         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
         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
         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');
         end else raise Exception.Create('Select Sale type');
         {%endregion}
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsDelistAccount) then begin
       end else if (PageControlOpType.ActivePage = tsDelistAccount) then begin
         {%region Operation: Delist For Sale}
         {%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:
         // 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}
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsBuyAccount) then begin
       end else if (PageControlOpType.ActivePage = tsBuyAccount) then begin
         {%region Operation: Buy Account}
         {%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');
           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}
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsChangeInfo) then begin
       end else if (PageControlOpType.ActivePage = tsChangeInfo) then begin
         {%region Operation: Change Info}
         {%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
         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,
              LChangeAccountData,LNewAccountData,
-             _fee,FEncodedPayload);
+             LFee,FEncodedPayload);
         end;
         end;
         {%endregion}
         {%endregion}
       end else begin
       end else begin
         raise Exception.Create('No operation selected');
         raise Exception.Create('No operation selected');
       end;
       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;
       end;
-      FreeAndNil(op);
+      FreeAndNil(LOperation);
     end;
     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;
         PChar(Application.Title),MB_YESNO+MB_ICONINFORMATION+MB_DEFBUTTON2)<>IdYes then exit;
     end else begin
     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;
         PChar(Application.Title),MB_YESNO+MB_ICONINFORMATION+MB_DEFBUTTON2)<>IdYes then exit;
     end;
     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
       If i>1 then begin
         With TFRMMemoText.Create(Self) do
         With TFRMMemoText.Create(Self) do
         Try
         Try
-          InitData(Application.Title,operationstxt);
+          InitData(Application.Title,LOperationsTxt);
           ShowModal;
           ShowModal;
         finally
         finally
           Free;
           Free;
         end;
         end;
       end else begin
       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;
       end;
       ModalResult := MrOk;
       ModalResult := MrOk;
     end else if (i>0) then begin
     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+
-        errors+#10+#10+
+        LErrors+#10+#10+
         'Total successfully executed operations: '+inttostr(i);
         'Total successfully executed operations: '+inttostr(i);
       With TFRMMemoText.Create(Self) do
       With TFRMMemoText.Create(Self) do
       Try
       Try
-        InitData(Application.Title,operationstxt);
+        InitData(Application.Title,LOperationsTxt);
         ShowModal;
         ShowModal;
       finally
       finally
         Free;
         Free;
       end;
       end;
       ModalResult := MrOk;
       ModalResult := MrOk;
     end else begin
     end else begin
-      raise Exception.Create(errors);
+      raise Exception.Create(LErrors);
     end;
     end;
   Finally
   Finally
-    ops.Free;
+    LOperations.Free;
   End;
   End;
 end;
 end;
 
 
@@ -461,20 +473,60 @@ begin
 end;
 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);
 procedure TFRMOperation.CM_WalletChanged(var Msg: TMessage);
 begin
 begin
    UpdateWalletKeys;
    UpdateWalletKeys;
 end;
 end;
 
 
 procedure TFRMOperation.ebAccountNumberExit(Sender: TObject);
 procedure TFRMOperation.ebAccountNumberExit(Sender: TObject);
-Var an : Cardinal;
+Var LEPasa : TEPASA;
   eb : TEdit;
   eb : TEdit;
 begin
 begin
-  if (Not assigned(Sender)) then exit;
+  if (Not assigned(Sender)) or (FUpdating) then exit;
   if (Not (Sender is TEdit)) then exit;
   if (Not (Sender is TEdit)) then exit;
   eb := TEdit(Sender);
   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
   end else begin
     eb.Text := '';
     eb.Text := '';
   end;
   end;
@@ -485,7 +537,7 @@ procedure TFRMOperation.ebCurrencyExit(Sender: TObject);
 Var m : Int64;
 Var m : Int64;
   eb : TEdit;
   eb : TEdit;
 begin
 begin
-  if (Not assigned(Sender)) then exit;
+  if (Not assigned(Sender)) or (FUpdating) then exit;
   if (Not (Sender is TEdit)) then exit;
   if (Not (Sender is TEdit)) then exit;
   eb := TEdit(Sender);
   eb := TEdit(Sender);
   If Not (eb.ReadOnly) then begin
   If Not (eb.ReadOnly) then begin
@@ -532,10 +584,10 @@ procedure TFRMOperation.FormCreate(Sender: TObject);
 begin
 begin
   {$IFDEF USE_GNUGETTEXT}TranslateComponent(self);{$ENDIF}
   {$IFDEF USE_GNUGETTEXT}TranslateComponent(self);{$ENDIF}
   FDisabled := false;
   FDisabled := false;
+  FUpdating := False;
   FWalletKeys := Nil;
   FWalletKeys := Nil;
   FSenderAccounts := TOrderedCardinalList.Create;
   FSenderAccounts := TOrderedCardinalList.Create;
   FSenderAccounts.OnListChanged := OnSenderAccountsChanged;
   FSenderAccounts.OnListChanged := OnSenderAccountsChanged;
-  FDisabled := true;
   FNode := TNode.Node;
   FNode := TNode.Node;
   ebSenderAccount.OnKeyDown:=ebAccountKeyDown;
   ebSenderAccount.OnKeyDown:=ebAccountKeyDown;
   ebSenderAccount.Tag:=CT_AS_MyAccounts;
   ebSenderAccount.Tag:=CT_AS_MyAccounts;
@@ -608,6 +660,24 @@ begin
   ebFee.OnExit:= ebCurrencyExit;
   ebFee.OnExit:= ebCurrencyExit;
   memoAccounts.Lines.Clear;
   memoAccounts.Lines.Clear;
   PageControlOpType.ActivePage := tsTransaction;
   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;
 end;
 
 
 procedure TFRMOperation.ebNewPublicKeyExit(Sender: TObject);
 procedure TFRMOperation.ebNewPublicKeyExit(Sender: TObject);
@@ -640,14 +710,16 @@ end;
 
 
 procedure TFRMOperation.searchAccount(editBox: TCustomEdit);
 procedure TFRMOperation.searchAccount(editBox: TCustomEdit);
 Var F : TFRMAccountSelect;
 Var F : TFRMAccountSelect;
-  c : Cardinal;
+  LEPasa : TEPasa;
 begin
 begin
   F := TFRMAccountSelect.Create(Self);
   F := TFRMAccountSelect.Create(Self);
   try
   try
     F.Node := FNode;
     F.Node := FNode;
     F.WalletKeys := FWalletKeys;
     F.WalletKeys := FWalletKeys;
     F.Filters:=editBox.Tag;
     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;
     F.AllowSelect:=True;
     If F.ShowModal=MrOk then begin
     If F.ShowModal=MrOk then begin
       editBox.Text := TAccountComp.AccountNumberToAccountTxtNumber(F.GetSelected);
       editBox.Text := TAccountComp.AccountNumberToAccountTxtNumber(F.GetSelected);
@@ -840,17 +912,15 @@ begin
   end;
   end;
 end;
 end;
 
 
-var GInUpdateInfoClick : boolean;
 procedure TFRMOperation.updateInfoClick(Sender: TObject);
 procedure TFRMOperation.updateInfoClick(Sender: TObject);
 Var errors : String;
 Var errors : String;
 begin
 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;
 end;
 end;
 
 
@@ -1147,6 +1217,8 @@ Var
   changeName,changeType, LRecipientSigned, LChangeAccountData : Boolean;
   changeName,changeType, LRecipientSigned, LChangeAccountData : Boolean;
   newName, LNewAccountData : TRawBytes;
   newName, LNewAccountData : TRawBytes;
   newType : Word;
   newType : Word;
+  LTargetEPASA : TEPasa;
+  LRequiresPurchase : Boolean;
 begin
 begin
   Result := false;
   Result := false;
   sender_account := CT_Account_NUL;
   sender_account := CT_Account_NUL;
@@ -1212,7 +1284,7 @@ begin
     end;
     end;
   End;
   End;
   if (PageControlOpType.ActivePage = tsTransaction) then begin
   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
   end else if (PageControlOpType.ActivePage = tsChangePrivateKey) then begin
     Result := UpdateOpChangeKey(GetDefaultSenderAccount,signer_account,publicKey,errors);
     Result := UpdateOpChangeKey(GetDefaultSenderAccount,signer_account,publicKey,errors);
   end else if (PageControlOpType.ActivePage = tsListAccount) then begin
   end else if (PageControlOpType.ActivePage = tsListAccount) then begin
@@ -1247,8 +1319,6 @@ begin
   lblSignerAccount.Enabled := ebSignerAccount.Enabled;
   lblSignerAccount.Enabled := ebSignerAccount.Enabled;
   lblChangeName.Enabled:= (PageControlOpType.ActivePage=tsChangeInfo) And (SenderAccounts.Count=1);
   lblChangeName.Enabled:= (PageControlOpType.ActivePage=tsChangeInfo) And (SenderAccounts.Count=1);
   ebChangeName.Enabled:= lblChangeName.Enabled;
   ebChangeName.Enabled:= lblChangeName.Enabled;
-  //
-  UpdatePayload(sender_account, e);
 end;
 end;
 
 
 function TFRMOperation.UpdateOpListAccount(const TargetAccount: TAccount;
 function TFRMOperation.UpdateOpListAccount(const TargetAccount: TAccount;
@@ -1508,177 +1578,249 @@ begin
   End;
   End;
 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
 begin
-  Result := False;
-  errors := '';
+  AErrors := '';
   lblTransactionErrors.Caption := '';
   lblTransactionErrors.Caption := '';
   if PageControlOpType.ActivePage<>tsTransaction then exit;
   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;
   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;
   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;
-  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;
   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 (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;
   end;
   end;
-  Result := True;
 end;
 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;
   i : Integer;
-  valid : Boolean;
-  wk : TWalletKey;
+  LValid : Boolean;
+  LWalletKey : TWalletKey;
+  LPassword : String;
   LPayloadBytes : TRawBytes;
   LPayloadBytes : TRawBytes;
 begin
 begin
-  valid := false;
-  payload_encrypted := Nil;
+  LValid := false;
+  LPayloadBytes := Nil;
+  LEncryptedPayloadBytes := Nil;
   FEncodedPayload := CT_TOperationPayload_NUL;
   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
   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;
     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;
-    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
       // 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)
       if (PageControlOpType.ActivePage=tsTransaction) or (PageControlOpType.ActivePage=tsListAccount) or (PageControlOpType.ActivePage=tsDelistAccount)
         or (PageControlOpType.ActivePage=tsBuyAccount) then begin
         or (PageControlOpType.ActivePage=tsBuyAccount) then begin
+
         // With dest public key
         // With dest public key
         If (PageControlOpType.ActivePage=tsTransaction) then begin
         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;
             exit;
           end;
           end;
         end else if (PageControlOpType.ActivePage=tsListAccount) then begin
         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;
             exit;
           end;
           end;
         end else if (PageControlOpType.ActivePage=tsDelistAccount) then begin
         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;
             exit;
           end;
           end;
         end else if (PageControlOpType.ActivePage=tsBuyAccount) then begin
         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;
             exit;
           end;
           end;
         end else begin
         end else begin
-          errors := 'ERROR DEV 20170512-1';
+          AErrors := 'ERROR DEV 20170512-1';
           exit;
           exit;
         end;
         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;
           exit;
         end;
         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
       end else if (PageControlOpType.ActivePage=tsChangePrivateKey) then begin
         if (rbChangeKeyWithAnother.Checked) then begin
         if (rbChangeKeyWithAnother.Checked) then begin
           // With new key generated
           // With new key generated
           if (cbNewPrivateKey.ItemIndex>=0) then begin
           if (cbNewPrivateKey.ItemIndex>=0) then begin
             i := PtrInt(cbNewPrivateKey.Items.Objects[cbNewPrivateKey.ItemIndex]);
             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
           end else begin
-            errors := 'Must select a private key';
+            AErrors := 'Must select a private key';
             exit;
             exit;
           end;
           end;
         end else if (rbChangeKeyTransferAccountToNewOwner.Checked) then begin
         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;
             exit;
           end;
           end;
         end else begin
         end else begin
-          errors := 'Must select change type';
+          AErrors := 'Must select change type';
           exit;
           exit;
         end;
         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
         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;
           exit;
         end;
         end;
       end else begin
       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;
-    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
     end else begin
-      errors := 'Must select an encryption option for payload';
+      AErrors := 'Must select an encryption option for payload';
     end;
     end;
   finally
   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;
     end;
     end;
-    if valid then begin
+    if LValid then begin
       lblEncryptionErrors.Caption := '';
       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
     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;
     end;
-    FEncodedPayload.payload_raw := payload_encrypted;
-    Result := valid;
   end;
   end;
 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
     If op.OperationToOperationResume(FSourceNode.Bank.BlocksCount,op,True,op.SignerAccount,opr) then begin
       jsonObj := TPCJSONObject.Create;
       jsonObj := TPCJSONObject.Create;
       Try
       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('JSON:');
         mOperationInfo.Lines.Add(jsonObj.ToJSON(False));
         mOperationInfo.Lines.Add(jsonObj.ToJSON(False));
       Finally
       Finally

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

@@ -30,11 +30,10 @@ uses
   LCLIntf, LCLType, LMessages,
   LCLIntf, LCLType, LMessages,
 {$ENDIF}
 {$ENDIF}
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
-  Dialogs, StdCtrls, Buttons, ComCtrls, UAppParams, UWallet;
+  Dialogs, StdCtrls, Buttons, ComCtrls, UAppParams, USettings, UWallet;
 
 
 type
 type
 
 
-  TMinerPrivateKey = (mpk_NewEachTime, mpk_Random, mpk_Selected);
 
 
   { TFRMPascalCoinWalletConfig }
   { TFRMPascalCoinWalletConfig }
 
 
@@ -98,7 +97,7 @@ type
 implementation
 implementation
 
 
 uses
 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}
 {$IFnDEF FPC}
   {$R *.dfm}
   {$R *.dfm}
@@ -108,7 +107,7 @@ uses
 
 
 procedure TFRMPascalCoinWalletConfig.bbOkClick(Sender: TObject);
 procedure TFRMPascalCoinWalletConfig.bbOkClick(Sender: TObject);
 Var df : Int64;
 Var df : Int64;
-  mpk : TMinerPrivateKey;
+  mpk : TMinerPrivateKeyType;
   i : Integer;
   i : Integer;
 begin
 begin
   if udInternetServerPort.Position = udJSONRPCMinerServerPort.Position then raise Exception.Create('Server port and JSON-RPC Server miner port are equal!');
   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);
     udInternetServerPort.Position := AppParams.ParamByName[CT_PARAM_InternetServerPort].GetAsInteger(CT_NetServer_Port);
     ebDefaultFee.Text := TAccountComp.FormatMoney(AppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0));
     ebDefaultFee.Text := TAccountComp.FormatMoney(AppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0));
     cbJSONRPCMinerServerActive.Checked := AppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].GetAsBoolean(true);
     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_NewEachTime : rbGenerateANewPrivateKeyEachBlock.Checked := true;
       mpk_Random : rbUseARandomKey.Checked := true;
       mpk_Random : rbUseARandomKey.Checked := true;
       mpk_Selected : rbMineAllwaysWithThisKey.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
     object tsBlockChain: TTabSheet
       Caption = 'Block Explorer'
       Caption = 'Block Explorer'
       ImageIndex = 1
       ImageIndex = 1
-      ExplicitLeft = 0
-      ExplicitTop = 0
-      ExplicitWidth = 0
-      ExplicitHeight = 0
       object Panel2: TPanel
       object Panel2: TPanel
         Left = 0
         Left = 0
         Top = 0
         Top = 0
@@ -1061,10 +1057,6 @@ object FRMWallet: TFRMWallet
     object tsNodeStats: TTabSheet
     object tsNodeStats: TTabSheet
       Caption = 'Node Stats'
       Caption = 'Node Stats'
       ImageIndex = 3
       ImageIndex = 3
-      ExplicitLeft = 0
-      ExplicitTop = 0
-      ExplicitWidth = 0
-      ExplicitHeight = 0
       DesignSize = (
       DesignSize = (
         857
         857
         438)
         438)
@@ -1134,10 +1126,6 @@ object FRMWallet: TFRMWallet
     object tsMessages: TTabSheet
     object tsMessages: TTabSheet
       Caption = 'Messages'
       Caption = 'Messages'
       ImageIndex = 6
       ImageIndex = 6
-      ExplicitLeft = 0
-      ExplicitTop = 0
-      ExplicitWidth = 0
-      ExplicitHeight = 0
       DesignSize = (
       DesignSize = (
         857
         857
         438)
         438)

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

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

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

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

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

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

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

@@ -31,7 +31,7 @@ uses
   LCLIntf, LCLType, LMessages,
   LCLIntf, LCLType, LMessages,
 {$ENDIF}
 {$ENDIF}
   Classes, Grids, UNode, UAccounts, UBlockChain, UAppParams, UThread, UPCDataTypes,
   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};
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 
 Type
 Type
@@ -134,7 +134,7 @@ Type
 
 
   TOperationsGridUpdateThread = Class(TPCThread)
   TOperationsGridUpdateThread = Class(TPCThread)
     FOperationsGrid : TOperationsGrid;
     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
   protected
     procedure BCExecute; override;
     procedure BCExecute; override;
   public
   public
@@ -152,6 +152,8 @@ Type
     FBlockEnd: Int64;
     FBlockEnd: Int64;
     FMustShowAlwaysAnAccount: Boolean;
     FMustShowAlwaysAnAccount: Boolean;
     FOperationsGridUpdateThread : TOperationsGridUpdateThread;
     FOperationsGridUpdateThread : TOperationsGridUpdateThread;
+    FWalletKeys: TWalletKeys;
+    FPasswords: TList<String>;
     Procedure OnNodeNewOperation(Sender : TObject);
     Procedure OnNodeNewOperation(Sender : TObject);
     Procedure OnNodeNewAccount(Sender : TObject);
     Procedure OnNodeNewAccount(Sender : TObject);
     Procedure InitGrid;
     Procedure InitGrid;
@@ -177,6 +179,8 @@ Type
     Property AccountNumber : Int64 read FAccountNumber write SetAccountNumber;
     Property AccountNumber : Int64 read FAccountNumber write SetAccountNumber;
     Property MustShowAlwaysAnAccount : Boolean read FMustShowAlwaysAnAccount write SetMustShowAlwaysAnAccount;
     Property MustShowAlwaysAnAccount : Boolean read FMustShowAlwaysAnAccount write SetMustShowAlwaysAnAccount;
     Property Node : TNode read GetNode write SetNode;
     Property Node : TNode read GetNode write SetNode;
+    property WalletKeys : TWalletKeys read FWalletKeys write FWalletKeys;
+    property Passwords : TList<String> read FPasswords;
     Procedure UpdateAccountOperations; virtual;
     Procedure UpdateAccountOperations; virtual;
     Procedure ShowModalDecoder(WalletKeys: TWalletKeys; AppParams : TAppParams);
     Procedure ShowModalDecoder(WalletKeys: TWalletKeys; AppParams : TAppParams);
     Property BlockStart : Int64 read FBlockStart write SetBlockStart;
     Property BlockStart : Int64 read FBlockStart write SetBlockStart;
@@ -230,8 +234,6 @@ Type
 
 
   { TBlockChainGrid }
   { TBlockChainGrid }
 
 
-  TShowHashRateAs = (hr_Unit, hr_Kilo, hr_Mega, hr_Giga, hr_Tera, hr_Peta, hr_Exa);
-
   TBlockChainGrid = Class(TComponent)
   TBlockChainGrid = Class(TComponent)
   private
   private
     FBlockChainDataList : TList<TBlockChainData>;
     FBlockChainDataList : TList<TBlockChainData>;
@@ -283,72 +285,76 @@ implementation
 
 
 uses
 uses
   Graphics, SysUtils, UTime, UOpTransaction, UConst,
   Graphics, SysUtils, UTime, UOpTransaction, UConst,
+  UEPasa, UEPasaDecoder,
   UFRMPayloadDecoder, ULog;
   UFRMPayloadDecoder, ULog;
 
 
 { TAccountsGridUpdateThread }
 { TAccountsGridUpdateThread }
 
 
 procedure TAccountsGridUpdateThread.BCExecute;
 procedure TAccountsGridUpdateThread.BCExecute;
 Var
 Var
-  l : TAccountsNumbersList;
-  i,j, j_min, j_max : Integer;
+  LAccountsNumbersList : TAccountsNumbersList;
+  i,j, j_min : Integer;
   c  : Cardinal;
   c  : Cardinal;
   LApplyfilter : Boolean;
   LApplyfilter : Boolean;
   LAccount : TAccount;
   LAccount : TAccount;
   LNode : TNode;
   LNode : TNode;
+  LAccountsList : TList<Integer>;
 begin
 begin
   LApplyfilter := ((FAccountsGridFilter.MinBalance>0) Or ((FAccountsGridFilter.MaxBalance>=0) And (FAccountsGridFilter.MaxBalance<CT_MaxWalletAmount)));
   LApplyfilter := ((FAccountsGridFilter.MinBalance>0) Or ((FAccountsGridFilter.MaxBalance>=0) And (FAccountsGridFilter.MaxBalance<CT_MaxWalletAmount)));
   FBalance := 0;
   FBalance := 0;
   LNode := FAccountsGrid.Node;
   LNode := FAccountsGrid.Node;
   try
   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
           while (j_min>=0) do begin
 
 
           LNode.bank.SafeBox.StartThreadSafe;
           LNode.bank.SafeBox.StartThreadSafe;
           FAccountsGridFilter.OrderedAccountsKeyList.Lock; // Protection v4
           FAccountsGridFilter.OrderedAccountsKeyList.Lock; // Protection v4
           Try
           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);
                     FProcessedList.Add(LAccount.account);
                     FBalance := FBalance + LAccount.balance;
                     FBalance := FBalance + LAccount.balance;
                   end;
                   end;
-                end else begin
-                  FProcessedList.Add(LAccount.account);
-                  FBalance := FBalance + LAccount.balance;
+                  if Terminated then Exit;
                 end;
                 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;
             end;
           finally
           finally
             FAccountsGridFilter.OrderedAccountsKeyList.Unlock;
             FAccountsGridFilter.OrderedAccountsKeyList.Unlock;
             LNode.Bank.SafeBox.EndThreadSave;
             LNode.Bank.SafeBox.EndThreadSave;
           end;
           end;
-            if j_max>=0 then Sleep(0);
 
 
-          end;
-          inc(i);
         end;
         end;
-      end else begin
+        inc(i);
+      end;
+    end else begin
         c := 0;
         c := 0;
         while (c<LNode.Bank.SafeBox.AccountsCount) and (Not Terminated) do begin
         while (c<LNode.Bank.SafeBox.AccountsCount) and (Not Terminated) do begin
           LAccount := LNode.Bank.SafeBox.Account(c);
           LAccount := LNode.Bank.SafeBox.Account(c);
@@ -895,6 +901,7 @@ procedure TAccountsGrid.UnlockAccountsList;
 begin
 begin
   UpdateAccountsBalance;
   UpdateAccountsBalance;
   InitGridRowCount;
   InitGridRowCount;
+  if Assigned(FOnUpdated) then FOnUpdated(Self);
 end;
 end;
 
 
 procedure TAccountsGrid.UpdateAccountsBalance;
 procedure TAccountsGrid.UpdateAccountsBalance;
@@ -939,7 +946,7 @@ var list : TList<TOperationResume>;
 begin
 begin
   list := TList<TOperationResume>.Create;
   list := TList<TOperationResume>.Create;
   try
   try
-    DoUpdateOperationsGrid(FOperationsGrid.Node,list);
+    DoUpdateOperationsGrid(FOperationsGrid.Node,FOperationsGrid.WalletKeys,FOperationsGrid.Passwords,list);
     if (Not Terminated) then begin
     if (Not Terminated) then begin
       FOperationsGrid.FOperationsResume.Clear;
       FOperationsGrid.FOperationsResume.Clear;
       for i := 0 to list.Count-1 do begin
       for i := 0 to list.Count-1 do begin
@@ -960,7 +967,8 @@ begin
   Suspended := False;
   Suspended := False;
 end;
 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>;
 Var list : TList<Cardinal>;
   i,j : Integer;
   i,j : Integer;
   OPR : TOperationResume;
   OPR : TOperationResume;
@@ -969,6 +977,7 @@ Var list : TList<Cardinal>;
   bstart,bend : int64;
   bstart,bend : int64;
   LOperationsResume : TOperationsResumeList;
   LOperationsResume : TOperationsResumeList;
   LLockedMempool : TPCOperationsComp;
   LLockedMempool : TPCOperationsComp;
+  LEPasa : TEPasa;
 begin
 begin
   if Not Assigned(ANode) then exit;
   if Not Assigned(ANode) then exit;
   AList.Clear;
   AList.Clear;
@@ -1060,6 +1069,13 @@ begin
       end;
       end;
     end;
     end;
   Finally
   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;
 end;
 end;
 
 
@@ -1067,6 +1083,8 @@ end;
 
 
 constructor TOperationsGrid.Create(AOwner: TComponent);
 constructor TOperationsGrid.Create(AOwner: TComponent);
 begin
 begin
+  FPasswords := TList<String>.Create;
+  FWalletKeys := Nil;
   FAccountNumber := 0;
   FAccountNumber := 0;
   FDrawGrid := Nil;
   FDrawGrid := Nil;
   MustShowAlwaysAnAccount := false;
   MustShowAlwaysAnAccount := false;
@@ -1090,6 +1108,7 @@ begin
   end;
   end;
   FOperationsResume.Free;
   FOperationsResume.Free;
   FNodeNotifyEvents.Free;
   FNodeNotifyEvents.Free;
+  FPasswords.Free;
   inherited;
   inherited;
 end;
 end;
 
 
@@ -1134,8 +1153,9 @@ begin
 end;
 end;
 
 
 procedure TOperationsGrid.OnGridDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
 procedure TOperationsGrid.OnGridDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
-Var s : String;
+Var s, saux : String;
   opr : TOperationResume;
   opr : TOperationResume;
+  LRectLeft, LRectRight : TRect;
 begin
 begin
   DrawGrid.Canvas.Font.Color:=clWindowText;
   DrawGrid.Canvas.Font.Color:=clWindowText;
   opr := CT_TOperationResume_NUL;
   opr := CT_TOperationResume_NUL;
@@ -1208,7 +1228,24 @@ begin
         Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
         Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
       end else if ACol=7 then begin
       end else if ACol=7 then begin
         s := opr.PrintablePayload;
         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
       end else begin
         s := '(???)';
         s := '(???)';
         Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfCenter,tfVerticalCenter,tfSingleLine]);
         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
   - Fixed bug on CacheMem when replacing initial position of buffer
 
 
   Version 1.2 - Jan 2021
   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
   - 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
 uses
   Classes, SysUtils,
   Classes, SysUtils,
+  SyncObjs,
   // NOTE ABOUT FREEPASCAL (2020-03-10)
   // NOTE ABOUT FREEPASCAL (2020-03-10)
   // Current version 3.0.4 does not contain valid support for Generics, using Generics from this:
   // 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
   // https://github.com/PascalCoinDev/PascalCoin/tree/master/src/libraries/generics.collections
@@ -74,16 +75,21 @@ type
     FAllowDuplicates: Boolean;
     FAllowDuplicates: Boolean;
     FOrder: Integer;
     FOrder: Integer;
     FCircularProtection : Boolean;
     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 MoveRange(var ASourceNode, ADestNode : TAbstractBTreeNode; AFromSource, ACount, AToDest : Integer);
     procedure MoveRangeBetweenSiblings(var ASourceNode, ADestNode : TAbstractBTreeNode);
     procedure MoveRangeBetweenSiblings(var ASourceNode, ADestNode : TAbstractBTreeNode);
     procedure BTreeNodeToString(const ANode : TAbstractBTreeNode; ALevel, ALevelIndex : Integer; const AStrings : TStrings);
     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);
     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
   protected
     FCount: integer;
     FCount: integer;
+    FAbstractBTreeLock : TCriticalSection;
+    FIsFindingProcess : Boolean;
     function GetRoot: TAbstractBTreeNode; virtual; abstract;
     function GetRoot: TAbstractBTreeNode; virtual; abstract;
     procedure SetRoot(var Value: TAbstractBTreeNode); virtual; abstract;
     procedure SetRoot(var Value: TAbstractBTreeNode); virtual; abstract;
 
 
@@ -91,15 +97,17 @@ type
     function NewNode : TAbstractBTreeNode; virtual; abstract;
     function NewNode : TAbstractBTreeNode; virtual; abstract;
     procedure DisposeNode(var ANode : TAbstractBTreeNode); virtual; abstract;
     procedure DisposeNode(var ANode : TAbstractBTreeNode); virtual; abstract;
     procedure SetNil(var AIdentify : TIdentify); 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;
     function AreEquals(const AIdentify1, AIdentify2 : TIdentify) : Boolean;
     procedure SaveNode(var ANode : TAbstractBTreeNode); virtual; abstract;
     procedure SaveNode(var ANode : TAbstractBTreeNode); virtual; abstract;
     function GetCount : Integer; virtual;
     function GetCount : Integer; virtual;
     procedure SetCount(const ANewCount : Integer); virtual;
     procedure SetCount(const ANewCount : Integer); virtual;
     function GetHeight: Integer; virtual;
     function GetHeight: Integer; virtual;
     property Count : Integer read GetCount;
     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;
     function FindChildPos(const AIdent : TIdentify; const AParent : TAbstractBTreeNode) : Integer;
+    procedure DisposeData(var AData : TData); virtual;
+    function DoCompareData(const ALeftData, ARightData: TData): Integer; virtual;
   public
   public
     property AllowDuplicates : Boolean read FAllowDuplicates write FAllowDuplicates;
     property AllowDuplicates : Boolean read FAllowDuplicates write FAllowDuplicates;
     function IsNil(const AIdentify : TIdentify) : Boolean; virtual; abstract;
     function IsNil(const AIdentify : TIdentify) : Boolean; virtual; abstract;
@@ -115,10 +123,13 @@ type
     function FindLowest(out ALowest : TData) : Boolean;
     function FindLowest(out ALowest : TData) : Boolean;
     function FindHighestNode: TAbstractBTreeNode;
     function FindHighestNode: TAbstractBTreeNode;
     function FindHighest(out AHighest : TData) : Boolean;
     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 Add(const AData: TData) : Boolean;
     function Delete(const AData: TData) : Boolean;
     function Delete(const AData: TData) : Boolean;
     function NodeDataToString(const AData : TData) : String; virtual;
     function NodeDataToString(const AData : TData) : String; virtual;
     constructor Create(const AOnCompareIdentifyMethod: TComparison<TIdentify>; const AOnCompareDataMethod: TComparison<TData>; AAllowDuplicates : Boolean; AOrder: Integer);
     constructor Create(const AOnCompareIdentifyMethod: TComparison<TIdentify>; const AOnCompareDataMethod: TComparison<TData>; AAllowDuplicates : Boolean; AOrder: Integer);
+    destructor Destroy; override;
     property OnCompareIdentifyMethod: TComparison<TIdentify> read FOnCompareIdentify;
     property OnCompareIdentifyMethod: TComparison<TIdentify> read FOnCompareIdentify;
     property OnCompareDataMethod: TComparison<TData> read FOnCompareData;
     property OnCompareDataMethod: TComparison<TData> read FOnCompareData;
     function BTreeToString : String;
     function BTreeToString : String;
@@ -130,6 +141,8 @@ type
     procedure CheckConsistency; virtual;
     procedure CheckConsistency; virtual;
     property Height : Integer read GetHeight;
     property Height : Integer read GetHeight;
     property CircularProtection : Boolean read FCircularProtection write FCircularProtection;
     property CircularProtection : Boolean read FCircularProtection write FCircularProtection;
+    procedure Lock;
+    procedure Unlock;
   End;
   End;
 
 
   TMemoryBTree<TData> = Class( TAbstractBTree<Integer,TData> )
   TMemoryBTree<TData> = Class( TAbstractBTree<Integer,TData> )
@@ -145,7 +158,7 @@ type
     procedure DisposeNode(var ANode : TAbstractBTree<Integer,TData>.TAbstractBTreeNode); override;
     procedure DisposeNode(var ANode : TAbstractBTree<Integer,TData>.TAbstractBTreeNode); override;
     procedure SetNil(var AIdentify : Integer); override;
     procedure SetNil(var AIdentify : Integer); override;
     procedure SaveNode(var ANode : TAbstractBTree<Integer,TData>.TAbstractBTreeNode); 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
   public
     function IsNil(const AIdentify : Integer) : Boolean; override;
     function IsNil(const AIdentify : Integer) : Boolean; override;
     constructor Create(const AOnCompareDataMethod: TComparison<TData>; AAllowDuplicates : Boolean; AOrder : Integer);
     constructor Create(const AOnCompareDataMethod: TComparison<TData>; AAllowDuplicates : Boolean; AOrder : Integer);
@@ -163,14 +176,6 @@ type
     destructor Destroy; override;
     destructor Destroy; override;
   End;
   End;
 
 
-  TIntegerBTree = Class( TMemoryBTree<Integer> )
-  private
-  protected
-  public
-    constructor Create(AAllowDuplicates : Boolean; AOrder : Integer);
-    function NodeDataToString(const AData : Integer) : String; override;
-  End;
-
 implementation
 implementation
 
 
 { TAbstractBTree<TIdentify, TData> }
 { TAbstractBTree<TIdentify, TData> }
@@ -178,32 +183,47 @@ implementation
 function TAbstractBTree<TIdentify, TData>.Add(const AData: TData): Boolean;
 function TAbstractBTree<TIdentify, TData>.Add(const AData: TData): Boolean;
 var Lnode  : TAbstractBTreeNode;
 var Lnode  : TAbstractBTreeNode;
   iDataPos : Integer;
   iDataPos : Integer;
+  LCircularProtectionList : TOrderedList<TIdentify>;
 begin
 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;
-  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;
 end;
 
 
 function TAbstractBTree<TIdentify, TData>.AreEquals(const AIdentify1, AIdentify2: TIdentify): Boolean;
 function TAbstractBTree<TIdentify, TData>.AreEquals(const AIdentify1, AIdentify2: TIdentify): Boolean;
@@ -220,7 +240,7 @@ begin
   j := Length(ADataArray)-1;
   j := Length(ADataArray)-1;
   while (i <= j) do begin
   while (i <= j) do begin
     mid := (i + j) shr 1;
     mid := (i + j) shr 1;
-    cmp := FOnCompareData(AData,ADataArray[mid]);
+    cmp := DoCompareData(AData,ADataArray[mid]);
     if (cmp<0) then begin
     if (cmp<0) then begin
       j := mid - 1;
       j := mid - 1;
     end else if (cmp>0) then begin
     end else if (cmp>0) then begin
@@ -255,24 +275,27 @@ var Lsl : TStrings;
   Lnode : TAbstractBTreeNode;
   Lnode : TAbstractBTreeNode;
 begin
 begin
   Lsl := TStringList.Create;
   Lsl := TStringList.Create;
+  FAbstractBTreeLock.Acquire;
   try
   try
     Lnode := GetRoot;
     Lnode := GetRoot;
     if Not IsNil(Lnode.identify) then BTreeNodeToString(Lnode,0,0,Lsl);
     if Not IsNil(Lnode.identify) then BTreeNodeToString(Lnode,0,0,Lsl);
     Result := Lsl.Text;
     Result := Lsl.Text;
   finally
   finally
+    FAbstractBTreeLock.Release;
     Lsl.Free;
     Lsl.Free;
   end;
   end;
 end;
 end;
 
 
 procedure TAbstractBTree<TIdentify, TData>.CheckConsistency;
 procedure TAbstractBTree<TIdentify, TData>.CheckConsistency;
 var
 var
-  FDatas : TOrderedList<TData>;
+  FDatas : TList<TData>;
   FIdents : TOrderedList<TIdentify>;
   FIdents : TOrderedList<TIdentify>;
   Lnode : TAbstractBTreeNode;
   Lnode : TAbstractBTreeNode;
   Llevels, LnodesCount, LItemsCount : Integer;
   Llevels, LnodesCount, LItemsCount : Integer;
 begin
 begin
   FIdents := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
   FIdents := TOrderedList<TIdentify>.Create(False,FOnCompareIdentify);
-  FDatas := TOrderedList<TData>.Create(FAllowDuplicates,FOnCompareData);
+  FDatas := TList<TData>.Create;
+  FAbstractBTreeLock.Acquire;
   try
   try
     Llevels := 0;
     Llevels := 0;
     LnodesCount := 0;
     LnodesCount := 0;
@@ -286,12 +309,13 @@ begin
     end;
     end;
     CheckConsistencyFinalized(FDatas,FIdents,Llevels,LnodesCount,LItemsCount);
     CheckConsistencyFinalized(FDatas,FIdents,Llevels,LnodesCount,LItemsCount);
   finally
   finally
+    FAbstractBTreeLock.Release;
     FDatas.Free;
     FDatas.Free;
     FIdents.Free;
     FIdents.Free;
   end;
   end;
 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;
 var Lchild : TAbstractBTreeNode;
   i, Lcmp, iLeft, iRight : Integer;
   i, Lcmp, iLeft, iRight : Integer;
 begin
 begin
@@ -308,15 +332,15 @@ begin
     if (ANode.Count=0) then raise EAbstractBTree.Create(Format('Inconsistent NIL node at level %d',[ACurrentLevel]));
     if (ANode.Count=0) then raise EAbstractBTree.Create(Format('Inconsistent NIL node at level %d',[ACurrentLevel]));
     if (AParentDataIndexLeft>=0) then begin
     if (AParentDataIndexLeft>=0) then begin
       // Right must be < than parent
       // 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',
       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;
     end;
     if (AParentDataIndexRight>=0) then begin
     if (AParentDataIndexRight>=0) then begin
       // Right must be < than parent
       // 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',
       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;
   end;
   end;
   if (MinItemsPerNode>ANode.Count) or (MaxItemsPerNode<ANode.Count) then begin
   if (MinItemsPerNode>ANode.Count) or (MaxItemsPerNode<ANode.Count) then begin
@@ -326,7 +350,7 @@ begin
   end;
   end;
 
 
   for i := 1 to ANode.Count-1 do begin
   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]));
       [i-1,i,ANode.Count,NodeDataToString(ANode.data[i-1]),NodeDataToString(ANode.data[i]), ACurrentLevel]));
   end;
   end;
 
 
@@ -360,7 +384,7 @@ begin
 
 
 end;
 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
 begin
   //
   //
 end;
 end;
@@ -375,12 +399,14 @@ end;
 
 
 constructor TAbstractBTree<TIdentify, TData>.Create(const AOnCompareIdentifyMethod: TComparison<TIdentify>; const AOnCompareDataMethod: TComparison<TData>; AAllowDuplicates : Boolean; AOrder: Integer);
 constructor TAbstractBTree<TIdentify, TData>.Create(const AOnCompareIdentifyMethod: TComparison<TIdentify>; const AOnCompareDataMethod: TComparison<TData>; AAllowDuplicates : Boolean; AOrder: Integer);
 begin
 begin
+  FIsFindingProcess := False;
+  FAbstractBTreeLock := TCriticalSection.Create;
   FOnCompareIdentify := AOnCompareIdentifyMethod;
   FOnCompareIdentify := AOnCompareIdentifyMethod;
   FOnCompareData := AOnCompareDataMethod;
   FOnCompareData := AOnCompareDataMethod;
   FAllowDuplicates := AAllowDuplicates;
   FAllowDuplicates := AAllowDuplicates;
   FOrder := AOrder;
   FOrder := AOrder;
   if FOrder<3 then FOrder := 3 // Minimum order for a BTree is 3. Order = Max childs
   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
   FCount := -1;                 // -1 Means there is no control
   {$IFDEF ABSTRACTMEM_CIRCULAR_SEARCH_PROTECTION}
   {$IFDEF ABSTRACTMEM_CIRCULAR_SEARCH_PROTECTION}
   FCircularProtection := True;
   FCircularProtection := True;
@@ -394,22 +420,35 @@ var Lnode, Lparent, Lparentparent : TAbstractBTreeNode;
   iPos, iPosParent, iPosParentParent, j : Integer;
   iPos, iPosParent, iPosParentParent, j : Integer;
   LmovingUp : Boolean;
   LmovingUp : Boolean;
   Lleft, Lright : TAbstractBTreeNode;
   Lleft, Lright : TAbstractBTreeNode;
+  LCircularProtectionList: TOrderedList<TIdentify>;
 begin
 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
   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 (Lnode.IsLeaf) or (LmovingUp) then begin
       if (IsNil(Lnode.parent)) and (Length(Lnode.childs)=1) then begin
       if (IsNil(Lnode.parent)) and (Length(Lnode.childs)=1) then begin
         // child will be root
         // child will be root
@@ -432,8 +471,7 @@ begin
       if (Not LmovingUp) then begin
       if (Not LmovingUp) then begin
         BinarySearch(AData,Lparent.data,iPosParent);
         BinarySearch(AData,Lparent.data,iPosParent);
       end;
       end;
-      if (iPosParent>0) //and (iPosParent<=Lparent.Count)
-        then begin
+      if (iPosParent>0) then begin
         Lleft := GetNode(Lparent.childs[iPosParent-1]);
         Lleft := GetNode(Lparent.childs[iPosParent-1]);
         // Use Left?
         // Use Left?
         if Lleft.Count>MinItemsPerNode then begin
         if Lleft.Count>MinItemsPerNode then begin
@@ -577,7 +615,13 @@ begin
       //
       //
       // Search Indorder predecessor:
       // Search Indorder predecessor:
       Lleft := GetNode(Lnode.childs[iPos]);
       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
       if (Lleft.Count>MinItemsPerNode) then begin
         // Inorder predecessor
         // Inorder predecessor
         Lnode.data[iPos] := Lleft.data[Lleft.Count-1];
         Lnode.data[iPos] := Lleft.data[Lleft.Count-1];
@@ -588,7 +632,13 @@ begin
       end;
       end;
       // Search Indorder successor:
       // Search Indorder successor:
       Lright := GetNode(Lnode.childs[iPos+1]);
       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
       if (Lright.Count>MinItemsPerNode) then begin
         // Inorder successor
         // Inorder successor
         Lnode.data[iPos] := Lright.data[0];
         Lnode.data[iPos] := Lright.data[0];
@@ -641,18 +691,49 @@ begin
 
 
     end;
     end;
 
 
-    LmovingUp := True;
+    if (Not LmovingUp) then begin
+      if Assigned(LCircularProtectionList) then LCircularProtectionList.Clear;
+      LmovingUp := True;
+    end;
   until (False);
   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;
 end;
 
 
 procedure TAbstractBTree<TIdentify, TData>.EraseTree;
 procedure TAbstractBTree<TIdentify, TData>.EraseTree;
 var Lnode : TAbstractBTreeNode;
 var Lnode : TAbstractBTreeNode;
 begin
 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;
 end;
 
 
 procedure TAbstractBTree<TIdentify, TData>.EraseTreeExt(var ANode: TAbstractBTreeNode);
 procedure TAbstractBTree<TIdentify, TData>.EraseTreeExt(var ANode: TAbstractBTreeNode);
@@ -665,32 +746,69 @@ begin
       EraseTreeExt(Lchild);
       EraseTreeExt(Lchild);
     end;
     end;
   end;
   end;
-  SetLength(ANode.childs,0);
+  for i:=0 to Length(ANode.data)-1 do begin
+    DisposeData(ANode.data[i]);
+  end;
   DisposeNode(ANode);
   DisposeNode(ANode);
   ClearNode(ANode);
   ClearNode(ANode);
 end;
 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
 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
   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;
       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
   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
     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;
 end;
 end;
 
 
@@ -702,59 +820,178 @@ begin
   raise EAbstractBTree.Create(Format('Child not found at %s',[ToString(AParent)]));
   raise EAbstractBTree.Create(Format('Child not found at %s',[ToString(AParent)]));
 end;
 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;
 function TAbstractBTree<TIdentify, TData>.FindHighest(out AHighest : TData) : Boolean;
 var Lnode : TAbstractBTreeNode;
 var Lnode : TAbstractBTreeNode;
 begin
 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;
 end;
 
 
 function TAbstractBTree<TIdentify, TData>.FindHighestNode: TAbstractBTreeNode;
 function TAbstractBTree<TIdentify, TData>.FindHighestNode: TAbstractBTreeNode;
 begin
 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;
 end;
 
 
 function TAbstractBTree<TIdentify, TData>.FindLowest(out ALowest : TData) : Boolean;
 function TAbstractBTree<TIdentify, TData>.FindLowest(out ALowest : TData) : Boolean;
 var Lnode : TAbstractBTreeNode;
 var Lnode : TAbstractBTreeNode;
 begin
 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;
 end;
 
 
 function TAbstractBTree<TIdentify, TData>.FindLowestNode: TAbstractBTreeNode;
 function TAbstractBTree<TIdentify, TData>.FindLowestNode: TAbstractBTreeNode;
 begin
 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;
 end;
 
 
 function TAbstractBTree<TIdentify, TData>.FindPrecessor(const AData : TData; out APrecessor : TData) : Boolean;
 function TAbstractBTree<TIdentify, TData>.FindPrecessor(const AData : TData; out APrecessor : TData) : Boolean;
 var Lnode : TAbstractBTreeNode;
 var Lnode : TAbstractBTreeNode;
   iPos : Integer;
   iPos : Integer;
+  LCircularProtectionList: TOrderedList<TIdentify>;
 begin
 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;
 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;
 var Lparent : TAbstractBTreeNode;
+  Lsecondary : TOrderedList<TIdentify>;
 begin
 begin
   Result := False;
   Result := False;
   if (Not ANode.IsLeaf) then begin
   if (Not ANode.IsLeaf) then begin
     ANode := GetNode(ANode.childs[iPos]);
     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;
     iPos := ANode.Count-1;
     Exit(True);
     Exit(True);
   end else begin
   end else begin
@@ -771,10 +1008,21 @@ begin
         Exit(True);
         Exit(True);
       end else begin
       end else begin
         // Search parents until parent iPos>0
         // 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;
         end;
         if iPos>0 then begin
         if iPos>0 then begin
           Dec(iPos);
           Dec(iPos);
@@ -789,25 +1037,46 @@ end;
 function TAbstractBTree<TIdentify, TData>.FindSuccessor(const AData : TData; out ASuccessor : TData) : Boolean;
 function TAbstractBTree<TIdentify, TData>.FindSuccessor(const AData : TData; out ASuccessor : TData) : Boolean;
 var Lnode : TAbstractBTreeNode;
 var Lnode : TAbstractBTreeNode;
   iPos : Integer;
   iPos : Integer;
+  LCircularProtectionList: TOrderedList<TIdentify>;
 begin
 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;
 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;
 var Lparent : TAbstractBTreeNode;
+  Lsecondary : TOrderedList<TIdentify>;
 begin
 begin
   Result := False;
   Result := False;
   if (Not ANode.IsLeaf) then begin
   if (Not ANode.IsLeaf) then begin
     ANode := GetNode(ANode.childs[iPos+1]);
     ANode := GetNode(ANode.childs[iPos+1]);
     iPos := 0;
     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);
     Exit(True);
   end else begin
   end else begin
     if iPos+1<ANode.Count then begin
     if iPos+1<ANode.Count then begin
@@ -822,10 +1091,21 @@ begin
         Exit(True);
         Exit(True);
       end else begin
       end else begin
         // Search parents until parent iPos>0
         // 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;
         end;
         if iPos<Lparent.Count then begin
         if iPos<Lparent.Count then begin
           ANode := Lparent;
           ANode := Lparent;
@@ -844,6 +1124,8 @@ end;
 function TAbstractBTree<TIdentify, TData>.GetHeight: Integer;
 function TAbstractBTree<TIdentify, TData>.GetHeight: Integer;
 var Lnode : TAbstractBTreeNode;
 var Lnode : TAbstractBTreeNode;
 begin
 begin
+  FAbstractBTreeLock.Acquire;
+  try
   Lnode := GetRoot;
   Lnode := GetRoot;
   if (Lnode.Count=0) or (IsNil(Lnode.identify)) then Exit(0);
   if (Lnode.Count=0) or (IsNil(Lnode.identify)) then Exit(0);
   Result := 1;
   Result := 1;
@@ -851,6 +1133,14 @@ begin
     Lnode := GetNode(Lnode.childs[0]);
     Lnode := GetNode(Lnode.childs[0]);
     inc(Result);
     inc(Result);
   end;
   end;
+  finally
+    FAbstractBTreeLock.Release;
+  end;
+end;
+
+procedure TAbstractBTree<TIdentify, TData>.Lock;
+begin
+  FAbstractBTreeLock.Acquire;
 end;
 end;
 
 
 function TAbstractBTree<TIdentify, TData>.MaxChildrenPerNode: Integer;
 function TAbstractBTree<TIdentify, TData>.MaxChildrenPerNode: Integer;
@@ -953,7 +1243,7 @@ begin
   FCount := ANewCount;
   FCount := ANewCount;
 end;
 end;
 
 
-procedure TAbstractBTree<TIdentify, TData>.SplitAfterInsert(var ANode: TAbstractBTreeNode);
+procedure TAbstractBTree<TIdentify, TData>.SplitAfterInsert(var ANode: TAbstractBTreeNode; const ACircularProtectionList : TOrderedList<TIdentify>);
 var iDataInsertPos : Integer;
 var iDataInsertPos : Integer;
   LnewNode, Lup : TAbstractBTreeNode;
   LnewNode, Lup : TAbstractBTreeNode;
 begin
 begin
@@ -965,6 +1255,9 @@ begin
     // Lup will be a new root
     // Lup will be a new root
     Lup := NewNode;
     Lup := NewNode;
   end else begin
   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);
     Lup := GetNode(ANode.parent);
   end;
   end;
   if Lup.Count=0 then begin
   if Lup.Count=0 then begin
@@ -988,7 +1281,7 @@ begin
   // Remove data&child
   // Remove data&child
   ANode.DeleteData(MinItemsPerNode);
   ANode.DeleteData(MinItemsPerNode);
   SaveNode(ANode);
   SaveNode(ANode);
-  if Lup.Count>MaxItemsPerNode then SplitAfterInsert(Lup);
+  if Lup.Count>MaxItemsPerNode then SplitAfterInsert(Lup,ACircularProtectionList);
 end;
 end;
 
 
 function TAbstractBTree<TIdentify, TData>.ToString(const ANode: TAbstractBTreeNode): String;
 function TAbstractBTree<TIdentify, TData>.ToString(const ANode: TAbstractBTreeNode): String;
@@ -1002,6 +1295,11 @@ begin
   Result := '['+Result+']';
   Result := '['+Result+']';
 end;
 end;
 
 
+procedure TAbstractBTree<TIdentify, TData>.Unlock;
+begin
+  FAbstractBTreeLock.Release;
+end;
+
 { TAbstractBTree<TIdentify, TData>.TAbstractBTreeNode }
 { TAbstractBTree<TIdentify, TData>.TAbstractBTreeNode }
 
 
 function TAbstractBTree<TIdentify, TData>.TAbstractBTreeNode.Count: Integer;
 function TAbstractBTree<TIdentify, TData>.TAbstractBTreeNode.Count: Integer;
@@ -1079,7 +1377,7 @@ end;
 
 
 { TMemoryBTree<TData> }
 { 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;
 var i,iPos,nDisposed, LDisposedMinPos : Integer;
 begin
 begin
   inherited;
   inherited;
@@ -1191,18 +1489,6 @@ begin
   Froot := Value.identify;
   Froot := Value.identify;
 end;
 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> }
 { TNoDuplicateData<TData> }
 
 
 function TNoDuplicateData<TData>.Add(const AData: TData): Boolean;
 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',
   UAbstractAVLTree in '..\UAbstractAVLTree.pas',
   UAbstractBTree in '..\UAbstractBTree.pas',
   UAbstractBTree in '..\UAbstractBTree.pas',
   UAbstractMem in '..\UAbstractMem.pas',
   UAbstractMem in '..\UAbstractMem.pas',
+  UAbstractMemBTree in '..\UAbstractMemBTree.pas',
   UAbstractMemTList in '..\UAbstractMemTList.pas',
   UAbstractMemTList in '..\UAbstractMemTList.pas',
   UAVLCache in '..\UAVLCache.pas',
   UAVLCache in '..\UAVLCache.pas',
   UCacheMem in '..\UCacheMem.pas',
   UCacheMem in '..\UCacheMem.pas',
@@ -36,7 +37,8 @@ uses
   UOrderedList in '..\UOrderedList.pas',
   UOrderedList in '..\UOrderedList.pas',
   UCacheMem.Tests in 'src\UCacheMem.Tests.pas',
   UCacheMem.Tests in 'src\UCacheMem.Tests.pas',
   UAbstractMem.Tests in 'src\UAbstractMem.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))}
 {$IF Defined(FPC) and (Defined(CONSOLE_TESTRUNNER))}
 type
 type

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

@@ -16,6 +16,16 @@ uses
    UAbstractBTree, UOrderedList;
    UAbstractBTree, UOrderedList;
 
 
 type
 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)
    TestTAbstractBTree = class(TTestCase)
    strict private
    strict private
    public
    public
@@ -37,11 +47,20 @@ type
 
 
 implementation
 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
 begin
-  Result := ALeft - ARight;
+  Result := AData.ToString;
 end;
 end;
 
 
+{ TestTAbstractBTree }
+
 procedure TestTAbstractBTree.SetUp;
 procedure TestTAbstractBTree.SetUp;
 begin
 begin
 end;
 end;
@@ -65,24 +84,22 @@ begin
   nDeletes := 0;
   nDeletes := 0;
   Lbt := TIntegerBTree.Create(True,AOrder);
   Lbt := TIntegerBTree.Create(True,AOrder);
   try
   try
+    Lbt.CircularProtection := (AOrder MOD 2)=0;
     repeat
     repeat
       inc(nRounds);
       inc(nRounds);
       intValue := Random(AOrder * 100);
       intValue := Random(AOrder * 100);
       if Random(2)=0 then begin
       if Random(2)=0 then begin
         if (Lbt.Add(intValue)) then begin
         if (Lbt.Add(intValue)) then begin
           inc(nAdds);
           inc(nAdds);
-          if Random(100)=0 then begin
-            Lbt.CheckConsistency;
-          end;
         end;
         end;
       end else begin
       end else begin
         if Lbt.Delete(intValue) then begin
         if Lbt.Delete(intValue) then begin
           inc(nDeletes);
           inc(nDeletes);
-          if Random(100)=0 then begin
-            Lbt.CheckConsistency;
-          end;
         end;
         end;
       end;
       end;
+      if Random(100)=0 then begin
+        Lbt.CheckConsistency;
+      end;
     until (nRounds>=AOrder * 10000);
     until (nRounds>=AOrder * 10000);
     Lbt.CheckConsistency;
     Lbt.CheckConsistency;
     // Delete mode
     // Delete mode
@@ -187,6 +204,7 @@ begin
   for Lorder := 3 to 7 do begin
   for Lorder := 3 to 7 do begin
     Lbt := TIntegerBTree.Create(False,Lorder);
     Lbt := TIntegerBTree.Create(False,Lorder);
     try
     try
+      Lbt.CircularProtection := (Lorder MOD 2)=0;
       valMin := 1;
       valMin := 1;
       intValue :=valMin;
       intValue :=valMin;
       Lregs := 0;
       Lregs := 0;
@@ -229,6 +247,7 @@ begin
   for Lorder := 3 to 7 do begin
   for Lorder := 3 to 7 do begin
     Lbt := TIntegerBTree.Create(True,Lorder);
     Lbt := TIntegerBTree.Create(True,Lorder);
     try
     try
+      Lbt.CircularProtection := (Lorder MOD 2)=0;
       valMin := 1;
       valMin := 1;
       intValue :=valMin;
       intValue :=valMin;
       Lregs := 0;
       Lregs := 0;
@@ -248,15 +267,12 @@ begin
         inc(i);
         inc(i);
       end;
       end;
       Assert(intValue=valMax,Format('Successor %d<>%d',[intValue,valMax]));
       Assert(intValue=valMax,Format('Successor %d<>%d',[intValue,valMax]));
-//      Assert(i=Lregs,Format('Succcessor count %d %d',[i,Lregs]));
       Lbt.FindHighest(intValue);
       Lbt.FindHighest(intValue);
       i := 1;
       i := 1;
       while (Lbt.FindPrecessor(intValue,intValue)) do begin
       while (Lbt.FindPrecessor(intValue,intValue)) do begin
         inc(i);
         inc(i);
       end;
       end;
       Assert(intValue=valMin,Format('Precessor %d<>%d',[intValue,valMin]));
       Assert(intValue=valMin,Format('Precessor %d<>%d',[intValue,valMin]));
-//      Assert(i=Lregs,Format('Precessor count %d %d',[i,Lregs]));
-
     finally
     finally
       Lbt.Free;
       Lbt.Free;
     end;
     end;
@@ -303,7 +319,7 @@ begin
       i :=1;
       i :=1;
       while Lbt.Height<Lorder+1 do begin
       while Lbt.Height<Lorder+1 do begin
         intValue := Random(100);
         intValue := Random(100);
-        DoInsert(intValue); // Lbt.Add(intValue);
+        DoInsert(intValue);
         inc(i);
         inc(i);
       end;
       end;
 
 
@@ -320,7 +336,6 @@ begin
       end;
       end;
       LCurrentTree := Lbt.BTreeToString;
       LCurrentTree := Lbt.BTreeToString;
       Lbt.CheckConsistency;
       Lbt.CheckConsistency;
-      if LLastTree = '' then Beep;
     finally
     finally
       Lbt.Free;
       Lbt.Free;
     end;
     end;
@@ -375,7 +390,6 @@ begin
         intValue := Random(intValue)+1;
         intValue := Random(intValue)+1;
         DoDelete(intValue);
         DoDelete(intValue);
       end;
       end;
-      if LLastTree = '' then Beep;
     finally
     finally
       Lbt.Free;
       Lbt.Free;
     end;
     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 SetAsCardinal(CardValue : Cardinal);
     Procedure SetAsString(StringValue : String);
     Procedure SetAsString(StringValue : String);
     Procedure SetAsInt64(Int64Value : Int64);
     Procedure SetAsInt64(Int64Value : Int64);
+    Procedure SetAsUInt64(UInt64Value : UInt64);
     Procedure SetAsBoolean(BoolValue : Boolean);
     Procedure SetAsBoolean(BoolValue : Boolean);
     Procedure SetAsStream(Stream : TStream);
     Procedure SetAsStream(Stream : TStream);
     Procedure SetAsTBytes(Bytes : TBytes);
     Procedure SetAsTBytes(Bytes : TBytes);
@@ -65,6 +66,7 @@ Type
     function GetAsBoolean(Const DefValue : Boolean): Boolean;
     function GetAsBoolean(Const DefValue : Boolean): Boolean;
     function GetAsInteger(Const DefValue : Integer): Integer;
     function GetAsInteger(Const DefValue : Integer): Integer;
     function GetAsInt64(Const DefValue : Int64): Int64;
     function GetAsInt64(Const DefValue : Int64): Int64;
+    function GetAsUInt64(Const DefValue : UInt64): UInt64;
     function GetAsStream(Stream : TStream) : Integer;
     function GetAsStream(Stream : TStream) : Integer;
     function GetAsTBytes(Const DefValue : TBytes) : TBytes;
     function GetAsTBytes(Const DefValue : TBytes) : TBytes;
   End;
   End;
@@ -186,6 +188,18 @@ begin
   end;
   end;
 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;
 function TAppParam.GetAsInteger(const DefValue: Integer): Integer;
 begin
 begin
   if IsNull then Result := DefValue
   if IsNull then Result := DefValue
@@ -357,6 +371,13 @@ begin
   If Assigned(FAppParams) then FAppParams.Save;
   If Assigned(FAppParams) then FAppParams.Save;
 end;
 end;
 
 
+procedure TAppParam.SetAsUInt64(UInt64Value: UInt64);
+begin
+  FParamType := ptInt64;
+  FValue := UInt64Value;
+  If Assigned(FAppParams) then FAppParams.Save;
+end;
+
 procedure TAppParam.SetAsInteger(IntValue: Integer);
 procedure TAppParam.SetAsInteger(IntValue: Integer);
 begin
 begin
   FParamType := ptInteger;
   FParamType := ptInteger;

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

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

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

@@ -43,6 +43,15 @@ const
   MaxSeconds = MaxMilliseconds div 60;
   MaxSeconds = MaxMilliseconds div 60;
   MinSeconds = MinMilliseconds 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 }
 { GLOBAL HELPER FUNCTIONS }
 
 
 
 

+ 4 - 0
src/pascalcoin_daemon.ini

@@ -63,3 +63,7 @@ ABSTRACTMEM_CACHE_MAX_ACCOUNTS=
 ;ABSTRACTMEM_CACHE_MAX_PUBKEYS : Integer
 ;ABSTRACTMEM_CACHE_MAX_PUBKEYS : Integer
 ;Max number of public keys to store at cache - Default 5000
 ;Max number of public keys to store at cache - Default 5000
 ABSTRACTMEM_CACHE_MAX_PUBKEYS=
 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>
     </Target>
     <SearchPaths>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
       <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)"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
     </SearchPaths>
     <CodeGeneration>
     <CodeGeneration>

+ 13 - 5
src/pascalcoin_miner.lpi

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

+ 31 - 13
src/pascalcoin_miner.pp

@@ -27,8 +27,7 @@ uses
   UBlockChain, UPoolMinerThreads, UGPUMining,
   UBlockChain, UPoolMinerThreads, UGPUMining,
   UPoolMining, ULog, UThread, UAccounts, UCrypto,
   UPoolMining, ULog, UThread, UAccounts, UCrypto,
   UConst, UTime, UNode, UNetProtocol, USha256,
   UConst, UTime, UNode, UNetProtocol, USha256,
-  UOpenSSL, UBaseTypes, UCommon,
-  DelphiCL;
+  UOpenSSL, UBaseTypes, UCommon, DelphiCL;
 
 
 type
 type
 
 
@@ -44,6 +43,7 @@ type
     procedure WriteLine(nline : Integer; txt : String);
     procedure WriteLine(nline : Integer; txt : String);
     procedure OnInThreadNewLog(logtype : TLogType; Time : TDateTime; ThreadID : TThreadID; Const sender, logtext : AnsiString);
     procedure OnInThreadNewLog(logtype : TLogType; Time : TDateTime; ThreadID : TThreadID; Const sender, logtext : AnsiString);
   protected
   protected
+    FOutputAsLogs : Boolean;
     FWindow32X1,FWindow32Y1,FWindow32X2,FWindow32Y2: DWord;
     FWindow32X1,FWindow32Y1,FWindow32X2,FWindow32Y2: DWord;
     FLock : TCriticalSection;
     FLock : TCriticalSection;
     FPoolMinerThread : TPoolMinerThread;
     FPoolMinerThread : TPoolMinerThread;
@@ -57,7 +57,7 @@ type
   end;
   end;
 
 
 Const
 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_DeviceStatus = 3;
   CT_Line_ConnectionStatus = 4;
   CT_Line_ConnectionStatus = 4;
   CT_Line_MinerValues = 7;
   CT_Line_MinerValues = 7;
@@ -100,7 +100,6 @@ begin
 end;
 end;
 
 
 procedure TPascalMinerApp.OnConnectionStateChanged(Sender: TObject);
 procedure TPascalMinerApp.OnConnectionStateChanged(Sender: TObject);
-Const CT_state : Array[boolean] of String = ('Disconnected','Connected');
 var i : Integer;
 var i : Integer;
   s : String;
   s : String;
 begin
 begin
@@ -120,8 +119,6 @@ begin
 end;
 end;
 
 
 procedure TPascalMinerApp.OnDeviceStateChanged(Sender: TObject);
 procedure TPascalMinerApp.OnDeviceStateChanged(Sender: TObject);
-Var i : Integer;
-  s : String;
 begin
 begin
   If Sender is TCustomMinerDeviceThread then begin
   If Sender is TCustomMinerDeviceThread then begin
     If TCustomMinerDeviceThread(Sender).IsMining then WriteLine(CT_Line_DeviceStatus,'') // clear line
     If TCustomMinerDeviceThread(Sender).IsMining then WriteLine(CT_Line_DeviceStatus,'') // clear line
@@ -153,6 +150,10 @@ Var i : Integer;
 begin
 begin
   FLock.Acquire;
   FLock.Acquire;
   try
   try
+    if FOutputAsLogs then begin
+      WriteLn(Format('%s %s',[FormatDateTime('hh:nn:ss.zzz',Now()),txt]));
+      Exit;
+    end;
     i := length(txt);
     i := length(txt);
     if i<=(FWindow32X2-FWindow32X1+1) then begin
     if i<=(FWindow32X2-FWindow32X1+1) then begin
       setlength(txt,FWindow32X2-FWindow32X1+1);
       setlength(txt,FWindow32X2-FWindow32X1+1);
@@ -280,9 +281,9 @@ var
   end;
   end;
 
 
   Procedure DoWaitAndLog;
   Procedure DoWaitAndLog;
-  Var tc : TTickCount;
+  Var tc, LMaxElapsed : TTickCount;
     gs,ms : TMinerStats;
     gs,ms : TMinerStats;
-    hrReal,hrHashing, glhrHashing, glhrReal : Real;
+    hrHashing  : Real;
     i : Integer;
     i : Integer;
     devt : TCustomMinerDeviceThread;
     devt : TCustomMinerDeviceThread;
     s : String;
     s : String;
@@ -297,7 +298,9 @@ var
       end;
       end;
       while (Not Terminated) do begin
       while (Not Terminated) do begin
         sleep(100);
         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;
           tc := TPlatform.GetTickCount;
           For i:=0 to FDeviceThreads.Count-1 do begin
           For i:=0 to FDeviceThreads.Count-1 do begin
             devt := TCustomMinerDeviceThread(FDeviceThreads[i]);
             devt := TCustomMinerDeviceThread(FDeviceThreads[i]);
@@ -307,7 +310,7 @@ var
               else hrHashing := 0;
               else hrHashing := 0;
               gs := devt.GlobalDeviceStats;
               gs := devt.GlobalDeviceStats;
               If ms.RoundsCount>0 then begin
               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!';
                 If (gs.Invalids>0) then s := s +' '+inttostr(gs.Invalids)+' ERRORS!';
                 WriteLine(CT_Line_MiningStatus+i,s);
                 WriteLine(CT_Line_MiningStatus+i,s);
               end else begin
               end else begin
@@ -427,7 +430,7 @@ begin
   FLock := TCriticalSection.Create;
   FLock := TCriticalSection.Create;
   Try
   Try
     // quick check parameters
     // 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
     if ErrorMsg<>'' then begin
       //ShowException(Exception.Create(ErrorMsg));
       //ShowException(Exception.Create(ErrorMsg));
       WriteLn(ErrorMsg);
       WriteLn(ErrorMsg);
@@ -440,10 +443,19 @@ begin
       Exit;
       Exit;
     end;
     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('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');
       Writeln('Execute ',ExtractFileName(ExeName),' -h for more info');
-      ShowGPUDrivers;
       Exit;
       Exit;
     end;
     end;
 
 
@@ -511,6 +523,11 @@ begin
   FDeviceThreads := TList.Create;
   FDeviceThreads := TList.Create;
   StopOnException:=True;
   StopOnException:=True;
   FAppStartTime := Now;
   FAppStartTime := Now;
+  {$IF Defined(WINDOWS)}
+  FOutputAsLogs := False;
+  {$ELSE}
+  FOutputAsLogs := True;
+  {$ENDIF}
 end;
 end;
 
 
 destructor TPascalMinerApp.Destroy;
 destructor TPascalMinerApp.Destroy;
@@ -537,6 +554,7 @@ begin
   writeln('  (Not needed for PascalCoin core, only some third party pools)');
   writeln('  (Not needed for PascalCoin core, only some third party pools)');
   writeln('  -u USERNAME');
   writeln('  -u USERNAME');
   writeln('  -x PASSWORD');
   writeln('  -x PASSWORD');
+  writeln('  --outputlogs=BOOLEAN (Show output style as a log)');
   writeln('');
   writeln('');
   writeln('Basic example GPU mining over multiple devices: ');
   writeln('Basic example GPU mining over multiple devices: ');
   writeln('  ',ExtractFileName(ExeName),' -p 0 -d 0,1,2,3 -s -n ABC');
   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,
   Interfaces,
   {$ENDIF }
   {$ENDIF }
   Forms,
   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',
   UAccountKeyStorage in 'core\UAccountKeyStorage.pas',
   UAccounts in 'core\UAccounts.pas',
   UAccounts in 'core\UAccounts.pas',
-  {$IFDEF Use_OpenSSL}
   UAES in 'core\UAES.pas',
   UAES in 'core\UAES.pas',
-  {$ENDIF }
   UBaseTypes in 'core\UBaseTypes.pas',
   UBaseTypes in 'core\UBaseTypes.pas',
   UBlockChain in 'core\UBlockChain.pas',
   UBlockChain in 'core\UBlockChain.pas',
   UChunk in 'core\UChunk.pas',
   UChunk in 'core\UChunk.pas',
   UConst in 'core\UConst.pas',
   UConst in 'core\UConst.pas',
   UCrypto in 'core\UCrypto.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',
   UFileStorage in 'core\UFileStorage.pas',
   ULog in 'core\ULog.pas',
   ULog in 'core\ULog.pas',
+  UMurMur3Fast in 'core\UMurMur3Fast.pas',
   UNetProtection in 'core\UNetProtection.pas',
   UNetProtection in 'core\UNetProtection.pas',
   UNetProtocol in 'core\UNetProtocol.pas',
   UNetProtocol in 'core\UNetProtocol.pas',
   UNode in 'core\UNode.pas',
   UNode in 'core\UNode.pas',
+  UOpenSSL in 'core\UOpenSSL.pas',
   UOpTransaction in 'core\UOpTransaction.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',
   UPoolMinerThreads in 'core\UPoolMinerThreads.pas',
   UPoolMining in 'core\UPoolMining.pas',
   UPoolMining in 'core\UPoolMining.pas',
   URandomHash in 'core\URandomHash.pas',
   URandomHash in 'core\URandomHash.pas',
+  URandomHash2 in 'core\URandomHash2.pas',
   URPC in 'core\URPC.pas',
   URPC in 'core\URPC.pas',
   USettings in 'core\USettings.pas',
   USettings in 'core\USettings.pas',
   USha256 in 'core\USha256.pas',
   USha256 in 'core\USha256.pas',
@@ -41,36 +89,7 @@ uses
   UThread in 'core\UThread.pas',
   UThread in 'core\UThread.pas',
   UTime in 'core\UTime.pas',
   UTime in 'core\UTime.pas',
   UTxMultiOperation in 'core\UTxMultiOperation.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}
 {$R *.res}
 
 

+ 50 - 31
src/pascalcoin_wallet_classic.dproj

@@ -125,31 +125,6 @@
         <DelphiCompile Include="$(MainSource)">
         <DelphiCompile Include="$(MainSource)">
             <MainSource>MainSource</MainSource>
             <MainSource>MainSource</MainSource>
         </DelphiCompile>
         </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">
         <DCCReference Include="gui-classic\UFRMAbout.pas">
             <Form>FRMAbout</Form>
             <Form>FRMAbout</Form>
             <FormType>dfm</FormType>
             <FormType>dfm</FormType>
@@ -201,21 +176,65 @@
         </DCCReference>
         </DCCReference>
         <DCCReference Include="gui-classic\UGridUtils.pas"/>
         <DCCReference Include="gui-classic\UGridUtils.pas"/>
         <DCCReference Include="gui-classic\UGUIUtils.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">
         <DCCReference Include="gui-classic\UFRMHashLock.pas">
             <Form>FRMHashLock</Form>
             <Form>FRMHashLock</Form>
         </DCCReference>
         </DCCReference>
-        <DCCReference Include="core\URandomHash2.pas"/>
         <DCCReference Include="gui-classic\UFRMDiagnosticTool.pas">
         <DCCReference Include="gui-classic\UFRMDiagnosticTool.pas">
             <Form>FRMDiagnosticTool</Form>
             <Form>FRMDiagnosticTool</Form>
         </DCCReference>
         </DCCReference>
         <DCCReference Include="libraries\sphere10\UCommon.pas"/>
         <DCCReference Include="libraries\sphere10\UCommon.pas"/>
         <DCCReference Include="libraries\sphere10\UMemory.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\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">
         <BuildConfiguration Include="Debug">
             <Key>Cfg_2</Key>
             <Key>Cfg_2</Key>
             <CfgParent>Base</CfgParent>
             <CfgParent>Base</CfgParent>

+ 7 - 1
src/pascalcoin_wallet_classic.lpi

@@ -232,7 +232,7 @@
     </Target>
     </Target>
     <SearchPaths>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
       <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)"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
     </SearchPaths>
     <Parsing>
     <Parsing>
@@ -240,8 +240,14 @@
         <SyntaxMode Value="Delphi"/>
         <SyntaxMode Value="Delphi"/>
       </SyntaxOptions>
       </SyntaxOptions>
     </Parsing>
     </Parsing>
+    <CodeGeneration>
+      <Optimizations>
+        <OptimizationLevel Value="2"/>
+      </Optimizations>
+    </CodeGeneration>
     <Linking>
     <Linking>
       <Debugging>
       <Debugging>
+        <GenerateDebugInfo Value="False"/>
         <DebugInfoType Value="dsDwarf2Set"/>
         <DebugInfoType Value="dsDwarf2Set"/>
       </Debugging>
       </Debugging>
       <Options>
       <Options>

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