Welcome To Our Shell

Mister Spy & Souheyl Bypass Shell

Current Path : /home/cgabriel/

Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64
Upload File :
Current File : //home/cgabriel/main.001

--  GENERAL DEFINITIONS_BEGIN
--*****************************************************************************
-- if file_open ~= nil then for zaehler = 0,31,1 do write_flash_string(1+zaehler*2048, string.rep(' ',2040)) end end  --  flash reset als allererste Aktion

--  Parkticket Firmware
--  vgabriel / cgabriel
--  Copyright 31.01.2021 IfT GmbH

GIT_COMMIT      = '0.99e (9efdfc75001)'

--*****************************************************************************
--private

SERIALNUMBER    = '9999999'  

sec_key = 'bd3edc6c024218258903d52eff2ee5f5dd5b0e9f211e210a3b75a4b0f9096f02fbae67e2c105aeef6a6fbf02944c7cc86a629d39f8dd9bf2f14f9ef8c8d55bca'
pub_key = 'b2b5a4f181bb19c4e81798bf43e4c988c308e85bf37996e3198aa121f6aa5221de9b2ed4e02e9c87e17cd14ed3100f0813833b73190e0232657f698e31930bdc'
ak1     = 'de9b2ed4e02e9c87e17cd14ed3100f0813833b73190e0232657f698e31930bdc'
ak2     = 'fbae67e2c105aeef6a6fbf02944c7cc86a629d39f8dd9bf2f14f9ef8c8d55bca'

--*****************************************************************************

FIRMWARE_VER    = 'V_16.7_edison'

PRUEF_VALIDITY  = '20261231'
QRCODE_VALIDITY = '20261231'

POINTS_PER_EURO = 0.8  --  1.0: 6,33 Prozent, 0.8: 5,01 Prozent, 0,65: 4,11 Prozent  (190/3000*100*PPE)
LANGUAGES       = { "DE","EN","FR","IT", "DE","ES","PT","NL",  "FR","FL","TR","PL",  "RO","DE","FR","IT",    "CS","SK","HR"  }
FLAGS           = { "DE","EN","FR","IT", "AT","ES","PT","NL",  "WA","FL","TR","PL",  "RO","Cd","Cf","Ci",    "CS","SK","HR"  }

COINVALUE       = { 0.00, 0.10, 0.20, 0.50, 1.00, 2.00, 5.00, }
CURRENT         = 'EUR'
CURRENTSHORT    = 'E'

HAS_KNOB        = 0

PAPIERBREITE    = 32

RESET_FLASH     = 0
DISABLED        = 0
SDCARD          = 1

function notify (text)  print("NOTIFY", text)   end

sec_key3 = sec_key  --  Asymmetrische Signierung des Flash
pub_key3 = pub_key  --  mit EC 25519

   -- wird ueberschrieben durch:

sec_key3 = string.sub(sec_key,65,96)  --  RC4 Signierung des Flash
pub_key3 = string.sub(sec_key,65,96)  --  das ist wesentlich schneller

sec_key1 = string.sub(sec_key,33,96)  --  Salsa20 Signierung für Ticketslots
pub_key1 = string.sub(sec_key,33,96)  --  Sasla ist aber nur symmetrisch, also kann man es nicht verwenden

SERIALNUMBERMOD4096    = SERIALNUMBER
SERIALNUMBERMOD4096HEX = string.format("%03x",SERIALNUMBERMOD4096)
SX1                    = string.sub(SERIALNUMBERMOD4096HEX,1,1)
SX2                    = string.sub(SERIALNUMBERMOD4096HEX,2,2)
SX3                    = string.sub(SERIALNUMBERMOD4096HEX,3,3)

TEST_SCENARIO          = 0   --  33

--*******************************************************************************

DEFAULT_price_per_hour_1     = '1.50'    --  Werkseinstellungen
DEFAULT_min_sum_1            = '0.50'
DEFAULT_max_park_hour_1      = '5'
DEFAULT_max_park_minute_1    = '0'
DEFAULT_tagesticket_1        = '0'
DEFAULT_pay_mode_1           = '2'

DEFAULT_price_per_hour_2     = '0.50'
DEFAULT_min_sum_2            = '0.00'
DEFAULT_max_park_hour_2      = '0'
DEFAULT_max_park_minute_2    = '00'
DEFAULT_tagesticket_2        = '1'
DEFAULT_pay_mode_2           = '2'

DEFAULT_tax_vat              = '19.0'
DEFAULT_max_zeilen           = '50'
DEFAULT_report_number        = '0'

DEFAULT_location1            = '12park'
DEFAULT_location2            = '12park.de, +49 911/148781-45'
DEFAULT_location3            = 'www.12park.de'

DEFAULT_pin_randomseed       = SERIALNUMBER
DEFAULT_pin_definitions      = ''      

DEFAULT_tarif_1_1            = ''
DEFAULT_tarif_2_1            = ''
DEFAULT_tarif_3_1            = ''
DEFAULT_tarif_4_1            = ''

DEFAULT_tarif_1_2            = ''
DEFAULT_tarif_2_2            = ''
DEFAULT_tarif_3_2            = ''
DEFAULT_tarif_4_2            = ''

DEFAULT_tarif_1_3            = ''
DEFAULT_tarif_2_3            = ''
DEFAULT_tarif_3_3            = ''
DEFAULT_tarif_4_3            = ''

DEFAULT_tarif_1_4            = ''
DEFAULT_tarif_2_4            = ''
DEFAULT_tarif_3_4            = ''
DEFAULT_tarif_4_4            = ''

DEFAULT_tarif_1_5            = ''
DEFAULT_tarif_2_5            = ''
DEFAULT_tarif_3_5            = ''
DEFAULT_tarif_4_5            = ''

DEFAULT_tarif_1_6            = ''
DEFAULT_tarif_2_6            = ''
DEFAULT_tarif_3_6            = ''
DEFAULT_tarif_4_6            = ''

DEFAULT_tarif_1_7            = ''
DEFAULT_tarif_2_7            = ''
DEFAULT_tarif_3_7            = ''
DEFAULT_tarif_4_7            = ''

DEFAULT_tarif_1_8            = ''
DEFAULT_tarif_2_8            = ''
DEFAULT_tarif_3_8            = ''
DEFAULT_tarif_4_8            = ''

DEFAULT_tarif_1_9            = ''
DEFAULT_tarif_2_9            = ''
DEFAULT_tarif_3_9            = ''
DEFAULT_tarif_4_9            = ''

DEFAULT_tarif_1_10           = ''
DEFAULT_tarif_2_10           = ''
DEFAULT_tarif_3_10           = ''
DEFAULT_tarif_4_10           = ''

--  function SET_DEFAULT ()   -- lokale Einstellungen

--      DEFAULT_location1            = 'Parkplatz Albrechtstr Atrium'
--      DEFAULT_location2            = 'Bad Marienberg'
--      DEFAULT_location3            = '12park.de, +49 911/148781-45'
    
--  end

CHAR27 = 'ABCDE'..'FGHJK'..'LMNPQ'..'RSTWX'..'Y'..'345679'
CHAR02 = '01'
CHAR64 = 'abcdefghijklmnopqrstuvwxyz' .. 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' .. '0123456789' .. '-+'
CHAR32 = 'ABCDE' .. 'FGHJK' .. 'LMNOP' .. 'QRSTU' .. 'VWXYZ' .. '13456' .. '79'

MASK62FORWARD      = {}
MASK62FORWARD["0"] = "0"
MASK62FORWARD["-"] = "1"
MASK62FORWARD["+"] = "2"

MASK62REVERSE      = {}
MASK62REVERSE["0"] = "0"
MASK62REVERSE["1"] = "-"
MASK62REVERSE["2"] = "+"

MAXTRANSMIT        = 212

if TEST_SCENARIO == 33 then
    DEFAULT_max_park_hour_1      =  '200' 
    DEFAULT_max_zeilen           = '-350'
end


--*****************************************************************************
--  GENERAL_DEFINITIONS_END
--*****************************************************************************
--  LIBRARIES_BEGIN
--*****************************************************************************

DIACRITIC_REPLACE_DE       = {}
DIACRITIC_REPLACE          = {}

DIACRITIC_REPLACE_DE["ä"]  = "ae"  
DIACRITIC_REPLACE_DE["ö"]  = "oe"  
DIACRITIC_REPLACE_DE["ü"]  = "ue"  
DIACRITIC_REPLACE_DE["Ä"]  = "AE"  
DIACRITIC_REPLACE_DE["Ö"]  = "OE"  
DIACRITIC_REPLACE_DE["Ü"]  = "UE"  

DIACRITIC_REPLACE["ä"] = "a"  
DIACRITIC_REPLACE["ö"] = "o"  
DIACRITIC_REPLACE["ü"] = "u"  
DIACRITIC_REPLACE["Ä"] = "A"  
DIACRITIC_REPLACE["Ö"] = "O"  
DIACRITIC_REPLACE["Ü"] = "U"  
DIACRITIC_REPLACE["ß"] = "ss"  
DIACRITIC_REPLACE["¿"] = "?"
DIACRITIC_REPLACE["á"] = "a"
DIACRITIC_REPLACE["Á"] = "A"
DIACRITIC_REPLACE["à"] = "a"
DIACRITIC_REPLACE["ă"] = "a"
DIACRITIC_REPLACE["â"] = "a"
DIACRITIC_REPLACE["ã"] = "a"
DIACRITIC_REPLACE["ą"] = "a"
DIACRITIC_REPLACE["ć"] = "c"
DIACRITIC_REPLACE["č"] = "c"
DIACRITIC_REPLACE["Č"] = "C"
DIACRITIC_REPLACE["ç"] = "c"
DIACRITIC_REPLACE["đ"] = "d"
DIACRITIC_REPLACE["é"] = "e"
DIACRITIC_REPLACE["è"] = "e"
DIACRITIC_REPLACE["ê"] = "e"
DIACRITIC_REPLACE["Ê"] = "E"
DIACRITIC_REPLACE["ě"] = "e"
DIACRITIC_REPLACE["ę"] = "e"
DIACRITIC_REPLACE["ğ"] = "g"
DIACRITIC_REPLACE["í"] = "i"
DIACRITIC_REPLACE["ì"] = "i"
DIACRITIC_REPLACE["î"] = "i"
DIACRITIC_REPLACE["Î"] = "I"
DIACRITIC_REPLACE["İ"] = "I"
DIACRITIC_REPLACE["ı"] = "i"
DIACRITIC_REPLACE["ľ"] = "l"
DIACRITIC_REPLACE["ł"] = "l"
DIACRITIC_REPLACE["ń"] = "n"
DIACRITIC_REPLACE["ň"] = "n"
DIACRITIC_REPLACE["ñ"] = "n"
DIACRITIC_REPLACE["º"] = "o"
DIACRITIC_REPLACE["ó"] = "o"
DIACRITIC_REPLACE["ô"] = "o"
DIACRITIC_REPLACE["õ"] = "o"
DIACRITIC_REPLACE["ř"] = "r"
DIACRITIC_REPLACE["Ř"] = "R"
DIACRITIC_REPLACE["ś"] = "s"
DIACRITIC_REPLACE["š"] = "s"
DIACRITIC_REPLACE["Š"] = "S"
DIACRITIC_REPLACE["ş"] = "s"
DIACRITIC_REPLACE["ș"] = "s"
DIACRITIC_REPLACE["ť"] = "t"
DIACRITIC_REPLACE["ț"] = "t"
DIACRITIC_REPLACE["ú"] = "u"
DIACRITIC_REPLACE["û"] = "u"
DIACRITIC_REPLACE["ů"] = "u"
DIACRITIC_REPLACE["ý"] = "y"
DIACRITIC_REPLACE["ź"] = "z"
DIACRITIC_REPLACE["ž"] = "z"
DIACRITIC_REPLACE["ż"] = "z"

--*****************************************************************************
--private

VOUCHER_M = 10 + tonumber("0x"..string.sub(sec_key,7,7)..string.sub(ak1,3,3)..string.sub(pub_key,13,13)..string.sub(ak2,12,12)) % 87

--*****************************************************************************
--private

VOUCHER_SIGNA_TABLE = string.gsub( [[

DGEE93TY3PN0SN39M7DNAR5AMS93ZKL1L6MPFNRQQNX4P865S0LYG76W9DMS236RFJNS4NJGN3EP6GE1B69P2DTZJPM3BJC883H035BBKH7YCZ9GPFF9RHKK
ZG9NE5MBSFPW1656P4B3LERDY08Y3ZSYC3RKS89RW12XBY57JJ0F20NF6M0FXL0PJG4PXANH727FXJ8C8WPTJQZYLB0YJ7NQQH89P8SA19506CNK8Q6ZMQHB
6CCR03PX1MZRD86LRH17T11NNBG56DK942WDMTPQPGPC2SK2QALRBMN7SGJ1YP2K9Z2RKWPJK8934BMG23G5AG4YB8TC50WN3HNWAQBYLLCX46A7X84L255M
DQAAXDSQTKT34DCXSHJQNNSXMPRYL5QSEJYRW6P01YPKL3C01WXY9EFRCF1KTNN84YQ7BMQCQ82CAQNB1X32NQ2C2F20F22CXT08PX6MTL63C60ELNNL8WS1
JCHC86H9CCRBP7S1H1DTBYMJR5XQRDRKJ42ZR7SCGAZF9MGAP1B79SYWZ01ZYTMSS1MS3Z2N86FY476H3XR5DYN1ZYJH9G83HEWYTHYRPGYHQ7L6MLH65H32
4966JKX5X48ZLPZEFHWWRPEMDWJ6ATENXZA35LS1XH4RZLL7WPEW8AYBCK2F7PX5GLSW6YXLJ35LJBHB7W5KCM94ZG3P0QL7C0E0ZRTLCP696NLW973MKL05
9NEJ86178M8XMNR6P7CS6KCDTZ76W8HG8WEEDMHBCFM6F89XKMDYZ4EJLHRNF40W71PZQGNB7528CZT22MXA0RR1HR13JH51JL4HTHRCXX7P6C9LAHG54GQ5
ZRS5HG8M90FXC0PPSA8TFY8LJXBMA63Q6WDNS75BQT87H56QHKRBSQJN9T5CCS7FCGFXPTMM2EK67YKNSSBY4TRA2MX8ZMW1W409SWNCQJ0SSLSR091CF1FJ
PSZ0ZSHAQ33J5BWETJ0BXL3APDX2LS3DRRYZS48JTAQP21CGANPLL7GEZAP91B81BP4WHGBK8A41B94WTHNQR03ZMBSQF4N8Z4YEDCFBXCD859YPAYMAGA8W
WHT4L10WSZQF128087WDEX30FA571CEFTM3QPN1JZRD427DT2Q5NWSFJY1Y9L548C1LB18TJ85MTJ723SRB3A8T7NWDENW33FKGGHJEF0BHDX7HE7R4WQAE8
CS333ZZQM3XX9RS4389QKA3T61FKBRHGS1R24ZTFYZKWHN6Y8DPHF7KMCEGZED30LCKX17STPR0GR8H87GF8PED3S1R0LC77HTMKCZ63H8RHEHFHDAF30NZ5
YZTM1CKJLGPSCAW14WJW6DA005N7LG4M3SCDX22YSWAXCD0TQE0KSMEL3DHKY5GDGYK6XC7EMWNA803L1PF6LYHKELP57F6P01SAL90B002W78N35XFBF3TY
JY81GKYBAA2ZCKRNL2W4191QHZ63YCDKD761T7DBWQRM4RM22R70WCX61LFTKN7X7WGCLYZPPZLZZ366C288Q9JRPCTJCD3G8RR31HBREJH9TNCLW73TD9T3
MXTAPLM558S80W8Y702CF8KJSW6X03Q98Y3M5A3BJ2SDZ4LPDN3R89KHGYB7D6D77HJ4P91HZ79DYPCEPZE5SXX5084RJWBY6TKSF6Q7W1Z2RYNP4ELPRRE0
Q20ZZQ4QRQJ0KZ8B4FMJ0MHD7W372J8T32KR9QPW5HJ0EZP275BQ3YBFXG8JEE64NYJTNHFLLCT6YYEB84AAK2LCSC9FJCC4E9ADSLAGKBQAC3W6TDT7PQ0J
7JD1W4SPAY31M24GMGMARYT9LXDDWYWEKQM3GH7840NFSH7ZDKQDSY8GF7Y4BX2991LKJHZ83ZWWEESKFJXEKT1DC9WGZHKKKFNQBG5GCNKJERQRJ9LQX83P
RM0ZXEMDR5B3WKNMJKG2SX2NZRQ786A8JMFC794BN10WA0YNEZ2F64FP4W60D92WNKM6B027ZSL8L5M62Y5EJTD8FG2RJHW48YRSZX9CEP6AEJTNKPDPYK50
LR0F4K3GAC58PN7DQWFC7H3TZ4Y4Q8DDQYNGS3B1HKNGK614DG9ZD5J1FSKWHX8GFM3EAGMQLMX040CWB5SHBNK8KN182N2JMMXHW10NPA0ZMNAK518KYZHA
GJJSJBN0S23C0PCXKN7GP95LXCF013549FQ6QQM4TS9HJ5BPLXFEHTA75588KD07NXWQ9YGFAK3R00PTSXMQZ4AWX41DWHG9BSYEAHYT8CTWN10SG9J36NML
HQAA08CQHECPWE1YJWXJFSP734FXHTYLY7ZZK1F85NMRDQ12TGBZM7PSRXE4Z52H4ADQ7RLJRBKEAAPBM7RC2NXX2RBAM5PFH32RCNJCBQJWK45GRMYX50JY
0T79PA4GX7GQ6LBXCTH2SXSA16EQMXN9G6009CBH0PG8EPTK1S36EWTME8L7TEDJZRESDH8YZ2QTWY6F6X5Z4P2WMREPES49AWBF6SGQDFRQG6W85RFFNK45
K2LXDXZF4RANAM80TQ24KHGG476H1R2A2REQX92B9E7CBSND4WWQ6SXNT5HW57DY7KP5BWKLQTKA7J0RBKPXRWX9RB05XRMMS5CWRWNQFJK8QEQ6FM5FYYHY
3C8QDEEF6JJ674S88NL5ECHYAXG7FCBP4SMWFBJM18JC0N7WPHRD1HLB7TX858MX4D43ZMAJDN9RPD2Q92327JMXA19HPR7ZDRY187S773B64MSA1WD3B6Q2
316B12RLX18T34DF1ZMK2ZRFSEPWZ8HRPP3RW3LHDCS4TXCW6A3L83144R6L8PLMQJM193K8ZN0K2NBESG64NB54B42AHR4GKF47P8KGMLLYLJ5GEHDEY62C
R4XH43DQQ9S8RRX85RP7KEQFWBR2XX8XB97YLZPDKKBSSMHG8GQHT5B0K3SC84QSAA6Z8ATHRD8S7FMK7EMD7SR1WSL1QD6KF9MWCPAC3JX0D1EGMBYK9160
7ZZS1PRZTMSLL1SKSDEX5A6CWB20CE709N52QM6YCTMSWZNCTEPP0S3JWME87Z2DZRP0MSG67W1LAYSBLJ6TNX691GRCC24YXHGT833S5DGZL5R7F7CF5FHQ
74707YGMJ3PZG7MWZ2A9RAD09JANDPQGZA7N7PMY7LHP4FTLJ3QKX3LH9ZZM00W35EB4CSJG0MFDTZS6YJ1FTMBX2R1K0X9TRLAD0RY7M0ZN3MAC07N4K7B3
7DWQN0YRT6D46EYJYH4DXN4A7HPTR7EBGQAGX6GZWYEDL52M2B64BRPF4QZ4S91MY7W02KBZA4TGXG2JPEM6L5M2E2BFGCTHG0CAEZQGER3RGMK0BZ84WDBQ
QF2FN0BG6GMZ3ECJLZKT2P4M000KRQ4P2722QMSS2H8896ZQQMY9LKNTE07EXYC4BQB6E9TNSNM3627SY3BM9BN8P7BWBZ6002MYZNY8P8AAT9YWLCRQGRL0
7JJX5RATX4W6H2JZTR829QZPBHMN43FR9CFNC51PXFA7KRP6RMEWL9T4ANHPBBNLGFEY1MRGTAQ6A8W6DPDA1MPB5G7KP600MYGPQZFR827JTEYMCLHLBGJK
3YNB7FQK0F4X7CXS8MPRBNMZEX94A6827M5XTED4RWBBM89EE1EH17DM58FHK97TRJZ9216XXXM1TR58T7FW2RRY8FJZ4YJ4KL6389QTZL2KLYZ8B71M718S
ZGMLZG2M9EARQJTFX0DYGTQNCN5G1D5JYCX4R40W25W900HA796PXSQPSEF6KTRA5XN53FR8TWRHJ4PYAANL1QD63RW9T23PJBL9QLRRPY44KHKYQK5R4A61
6EXCPQBHA4SK2AKW7Q6101TX6PXNK5Y16FEAWHQDL4XRNDW6BEBPME24CJH2FJFMCYH84GBXEMFD64GX2NWYDHKJ5MBT0QNRPZQHCABJH8WNGK2SJH62EYB7
80C2X32WYTAD3W211ZYHMX8G7JAHS2N4KC7CZ584PJW7BZ3CDC17PNP64KNP3PZ9BNMRLDJEWBH5NYSDE6GDAFGNE0P4X068FJQ12ZMHRX2NFCDY01LMZT5T
HEAB1TSZ6JJE5095SG5LYAMG7BWPDR1TL7EMD0X0D3YAPWZKSWFAD9MGEY0KYZ75NZCCBQNH6RQA20RCK0F24TAWWM32XFY2BD80B83X87ET8E5H117K2W3Z
RG6F77JFZZANXX295PFDD1TL9D0Z8PCQ00WQQBWFRDXFPJ4L43SWDBP8ZFXFXPWAFZJWRB4RZB0GTCTSZB3T0BNBQX8921CJ4974LRJW3DKL47GLFN8AB826
9TDGW4L76HYZ4KSTXR83B2K82Z4MF74HC9C86SK98SKGRR4FYGP3A4AELN7Y7R40W92DB3MA2Z8XHGS4BJT28BP3BACX69AEFDKQ4ZK7DZMT3HAG5BSC0KHQ
R197X3ZS45452QJQ0GYPC70AX5FA0BKGD7QP0FJDT8A0LRX1G0YR8Z69W53KJ9L2ED91N6LTKZ40ZBH3Q4C5LFPNT7J6EFEAN87NK41NESFTWBL11SQMLM58
KFKFAXLS9HKBL67A1M3XKKGANX4LTXC6SW5YH76SRY95BEHD71NTA03AA1GBGYXM2187CJ9CS0A9YQ8BNMEA941CS4Z5J7C336FZY41ACBCD66HA49T6620T
BBP1FBEQ2Y0FJHL9RSM6TF9A4F9NJ4111RWGBQFMP6T4PQANJ4AA3CT1PX1H3WKEWBT56ZCSQLQ8DKMW84D6T5311DKE8PS16BQ9N73Q475913GN1YA282FN
PDDM3FQF8MH8J1583BRE0ZDHH070SMFJ7RG0FF6WTNY6YMJDKKKL5X22SLSY3LS0L5711WX290EW928B3HJK73C9JXW9R34LSR872TLGHASAE42JL3P4GBAL
8DQ84ZCQH6A6CG8T84BB5M1BSRYM37BAGAJD8930C2M4AES4WWYRR9BA93H5FRH765ZKPYE3F71LH7X6EF6L42Z68DHWEELZ795A26XJ2GF6QK9CDCSWQMD5
7TRH8LDKPAY99F7G7M8AGYF1DEX3BPSX1S7PGZJ08KJREYZZQXQXFWGRQACAS2L6D4A1ENZ3J5C2MY6EFKRABGA68F9HJQN2GSF1KBEPJJW0KD1EXTG84MK0
97C6S3L5WYQ3337T9E47SSHD7CBR6TGCCR0XC1189RAL39Q5QZ44M5WJSLKTQ5LBMTMAWP40PNT7TRJRHMJXC7CX42GZRTN82MJHLM3D5LZP5LN23JJH197S
ZQMZRMFXDC7SA0FH1BZ8JZTTLAKM31ZRRXHR1BFLGMG8W70FX39CRXL5L03JHYKHF84GSC9MAKED11NGD9RDH5AW9XBAFTPSNZMSYRH7K9Z62GQPJPCMLFRQ
KBKEE4FDL9FM93JB552Y99N713JMQKL4T0WMCJ7K68GDALZZ76HECHHLYM3NGKHJE60JRQP7L0GZ1CCCFTKNKMEAC4AY8PKBSLTSCYTSGJM49694C7H2JWDW
60XKXC9DSFFXGHDB571NW2HMLLYFPL4A1BYGZ21MJZ1ED5464K663PJKESDC0WFHL0DWKLYQ9938PTKH6YPFR4DEQJKX7D74RG7K9B40ETWD2BTEC1YCKEK4
2PB53XFC6M2DK7RFC2RQMTCA2DF3KD1C3DSZNEY67X0TMGH6SHKZSW8C89ZH8J1627QWZNDNXRT1GSBM8T2H0T1WFJA9QBDTXB06BR6MYCQH26X1C4KNA6W5
YDNNZCT6GHWPHE54JELBGYDYKXRRL25MQL5FSQ550RLCEB92HPMDN6EJLLJ7N0J8TFW72RJKRFWE8GN0F5RXHE32A95A4F4AA69D67X7M4ZEDGYNWY2658EZ
R6Q4MGNHQTTSDZT7EKZK5HJNJ3AFA0BA8AND8P9L1FGL744Z8E5CW037PX8SSSY41YWPN0E1A18WK0SH1ZTXBLM7RDTAXWPG29F24Y0NTW97K3RARTE3870Q
ZHJ764QEA2NNXFPGJTPPEPKLYFJP8DMX9G5DDDS0PKFWEXKXG4W1ZB84QGHL1FYX68PGBJB6P8DD9XPJBTARE4J3E5KMTA1C4YYYS37J6Z2C69WXXCFR9C38
AWE3C5Z98HWHYLMBWYGD2KN42XJGPTXS0NWR5S9D8GTP9N70YQYX4R3BXTNWXX3SXMAYZ3EFR8X985JQEK92KL5DQ5Q9Y7BFJ2GLMT1MR0R89K58PP1JEQ23
ETLLCJ19B4PSAYHAG4JJ8FZMXJGNHS397KQGPRK74JTNKSS5QN05XB3J1NJKKT7T7AJAAP9NHFPMEK3CJPSC7WWC97WBDEEG028M9RMS0LHY01DEYFJB4BZ6
FS2N0PG7WXJ0F05ATX7H0ZWZ7BNRFZ7S8LSWEMEQ1XXGSYMBFJY6L2Q45Q4TFY7959G7N1AYGEZ55XJTCKCSC7QSAZ75HXPXZ05GHHHCWESNNNT3KLWW4TCP
JGE5AD1QR8FH0BTJHBJH7A1TGF0EZRCLGKB82C5X71B8CDZTZLKNGAT3LH4W49PD77B5GJYQSC5XJWGLP5L0Z8NW0HGCA0JW81N2AY9YDP0W97NT4LHLCZNR
EGC07WJSFJDLK8MXM94NE4122KNQ5NQ70BQQLH415WMGYCA8MN87HPL7JLS0EJ811LN824PR65GBA8SHMHNXZEM56ZTG5NH8LZLNEERZ7GN9XSS1QSG00WTM
S2WG26XNPYNDLN1HG1AA6Y7K6QLM18C718P0K2F4FFWYXXJA0N18YCHBBWKDY2Z9TG47ACYLP2M240DD05BZARNWWKQXCFZL6LHTKJ8JBDBYYHLZ2JCEJCPB
WM8MTF3CY15ASCN9J72SNRM2JANQFQ64EJ08CN7EY406T0C6QQ1QQP9HKT0EYM2LGKCYJ0LSCT15HE40CKX74ZRQJ0CKW80KZRS50GGR4HM5WF98DZKWCSTW
LL3N3NKYNRM0G2GE7W6L4C3W1WZL3JNKFBJQBQNBQMPFYFR346JM09RZ0ZWN59JTL8R45RYEMNRQFR637RAQ960JQJ4E6MMZE8L7QMHE5H3T4FLYFGP48X85
307FWJBPZ46FFPYTZC2EPR5XGE32RARCM7PY17RJR6C0SD4MJBB4BJBPXGWX82QE5858K5GS4Y5PLF5X8JBAQ0SWB3HSMGF3XT6DQQXGN10EGTN2ABEKPFB6
QY1LHZKBJYZHN7DP3RCNTC23GN9FXLM9MQQF048DJXT455HSM1SPED7TCEHQ6X149R344AWML00B939NE3QSHABTGRR8XTGHTR5S6NHZ0CN4H9H95G3X80G2
GZTAJ4P64ETNEKZ12GK6K8DGX4ATDMGY24735AXFGGF1579RPB74AZDEE08K92AZB9YKCFDR7NTJ6FKS8TP4JC2XN1D90FSQHAH159T4T28CJZXZJWLQ8N98
1MYZTAFS1378LCGQ827Z65CRRKPQK1LAP581ZW6JMWGB83ALMW94NMJRGHQLKCZ4AFK8J1Z0F3P9GSQ231B3Q13FK61N04453QD9N14FEHH18G064QXGRQ6S
S3KL18BE5Z85JG12DK07562XKJAKH9N8LK38HG3236RT81G7A7XZWJ738W9F09WT7MRNW32MX2P9F0E58R21JLDZP8BFRLHBBHBLYMB0Y74GQWWLWZMB97NT
K7ZS4DWY95M23SKC9QYY125FAC8C08LESW0QQJPYF5XQGDBJB3KDMGNHR8TGLQ101H2R5W1TRGRGYBL3G04W746703QT6RH3H73XLEGL1Z9TRPLGX2K5B1JQ
DHYG44N5Y3YM4H3SDLBDE4YNBAL0BF2ZBFCY3CMDHQ650FGR1PE5HLLYZ7GNMAZSLY6KEJX9Z5P31FC9XJP7FE37ZN6XSPDGRGLEEY89M5LQTKYGPRQ2E6LT
6HZTZ14Z4XTWFN0TLTY871XAT9DMDAN0PG4045B51DNZD0KA4SJR736CLGAAMSM5MFTFTE12S1AXHRELA2L9MH53CHX6Q1HEGSRJ0TBLWWR62LZDNW8MADAX
TE5Y52LWD57RPF59J5CBBYHB2FKENRRTE597YPEELHLEB781J6DTBTD5Z27ZX7JN477J6ZXHEY9HBRKT722X0Q1393RZFKZ3TP93GZY14WK1WPWE9HRJG19K
D95RB2CDW5HK2JA77Y3LNM69F676BDYH88HS11XXQBDT10DCGHK0FYJS7Y1FE9BBRYWTT4ZRY199HM5CFGRSZJRNSTX0X8QFQTHYSD1652D216QG8Q5FHMX8
NKW2HTKGXMZD7JG4BYBDBAYSK8EMDRECSPQ814X0F7YMYH0CJQ41JK7QZZETHA58SDL6WR847P03G6ZLKLMQFJM75BC2WAT30QFJQ9MATW24D6XXZ19BARXH
BP7LKCKE7E2AQ1TRXGJN832FEZ8GZ9CN10L6R0ZBY62N30N87J8ST1E8J32LLQL8X7JFQ58NHTPYHPPRH5ASQ0HHN72EANBE7TYSPQKGYJHJHZJE2NFT03SF
0CR5C370LB5D6G17EKY95SQRS29LMB1WGTNRE7G6FW05KHJFMS43RGCSS5GHJHALKA5FWWM4GWF5AC1HXK1F5EEZ6TDYS0MRM30YM9JW0DN5J1X3A0QHYCDB
LK3GLAGBX7C1D9FYMXM0GTN8H8FJ6N1HKECF0RQAB77HDM47TFQ4H8GEL62Z60EYYJ0ZK2QTLSKAH8WKWTQANXGL5S9H58J8ZEF5PWQE34GY0KAK6KR6DGHA
MSP34H6L957TNA8BP2CFCFD0EE8KTFYNLRBXHXRAYB3RW639EHHKB9LH838NPQNDQ7NG5N88768L9AQQQ0H6JWNGMMGLL0ADQA1ESNB9AAPC1M7RWR6726PY
J0MX64NSP1FKYQB7J60AJQABQ9FTD2WWKY41JH64KMQSLA9X9JL7ZZXFSKZXCAH265FYXMKCDHXSPZETLAC98P4HJLB9GYZ8MBP1S4EXLREDHSMW1AGPKDZP
AR1MYJE81CN6TE5QE6391SG913TA4KKPK6LSYA4TFHCH9S0Q1NYWKH0AYHTJ87HZ228XEGZYCY4M6CLBCMLWXTD0S5DH49EBZBM9P2W3F6A50GKNW0944MCQ
CJ097YENCBTBMFGT5MP92JTHEFLW9S1MNHN6JD6WZ352P9GLR0Q2EY6S0H3J8E0MWFJBRYLHC77BEPJEXZPNHSSC8W9LP439CTEC9ATMWCSQBP38G78C9H4Y
6A5SNK3C74HENSB9291M1DDHZ7TSNS6614T18EESWW093Q08YZD9TJAK13N126B0NDZ8S9H30C5N79M3S6L7YZTTRT2X35SB6MS1NQE076FXDT8X3CMCYP51
1R0DMLH438S35FPJM236GA5NF3C0BAHC6C44K5MWCZ5A7LNJNBYG2MGJSREW69M9506GT4S5EGZHTG5TN84WDDBFEH4ZA0KH7YDCKXAX6873XJL2ZXH1PRZD
4L1N16ZCBTHTGPZ85N61JH0L7C8ADF09YZT9QK566X3QWY5RRH1KLHFH1WQL4XPJ616SA419BE1L585M08G106JZDH997T7WWWCD7L8X6PYH5MXTEAWY9BH8
RARB3755LS6HGJB8H302AAG0TSZX9ETW09LNE7L69ZPJL7ZW03H52C9G2SAZF3S6L0A6QG91CTSSC8CR61MEEN1NKBGEG5G99MC12598P3HBAT3TWS8PSNQR
BFEP111XQL0TB4R614XBS8EC4BBQB6QF53XQDGW3TJLE8LZN26BDKH48RYL3BCQJHWH9KAL7R6Z8ZC860SGL8DCQLK642SM73XDWLSEL19CJM6YW1ZPNTB88
9K0ZR9PCH3M22BN0L3EBX55GWL0FJ29634PC6QHN7XWL8W135MG1394MQDTZQFP7C8005C04QK25B0KKWLA5PN3MCLXBYWXE1AYQXGGW5ANJMKQR0K5XJKT9
FGEGSRT769S5KMN0HNEXK3FE91R76TDMJSWNAF7T49QQNQXHQNBHB89LN9HAF9HD36C2M0G0JXX4WJ51JKJTZ78GE08RJ0ET8J0EEBYX8F3YDKTWP6FGWW3P
JAN85DRD2GKNPS25LMMAGNC0AN7D9FC753SBHSH65MALHD7W7KCQJ4XRH5EATJWQT3AS2PTYLB5WH4ZN8YQZK9H8N9J177NCFAXKKGSFNGJHELHBM0RGJHXJ
A3S9FKMAY1RSHALHMXEBY9F8J0XQ3JZ2TMGHZ2Q4FPP3SDYZNLNRQFQH690X7BJCSLTMNY0XNX8KNPMPDMN39QYCYGPBNHZK2JZWKB4KMGN5FMLRDE38CD6D
1AHYPEA9X8GCSE2Z122ZYNZLEY3JLFWML6QEGJQ6GXF5NK213H4DZLSTMWAB4DSXG4N3M8X2TSYTNX23B7HR4Q9F1CQKJ4DE5CJC638C2Q52WRM5BF51WPGX
BXD1BJMKW0WENYJWN0ZWGXNPKLY3EZQ473KF9ZE0BQY7NSLNL99THT18QTA3KJTT96HFP966172Y9NRP9W1SF9H2FQ8EHNMAAFLKHZ1JNFSTFSYH3TQPF09A
XR0BNWWA4NY5G90GHLJ3GKYR7NP8FT4S9D9980CG0D9BM4PF2HPA8RASNSHXB5DEA8Q427DKZ8TX0YSSJJYZJ7M49F3E14A70JYS1LN43T3NWWS776P1WLS0
Y21977DQ1L4W647FSFX3JE0YGX9KYBLT58XJKNZ6LLDZ2ZBC78HYB3TP8FJP8F1D9G2B0A2TZPKA0TFQN3NBG16XZY2X0FNJ6W18D0NTFJ366QKA7K2Q6LWQ
5Z99796WG31YPY9XSLL1D0BGE5FG74XJEH6HSWAM5B8L3YR2M5F7TQPMD22ZB4LHLZEGSGWZP1WB9W5NRT7TRANEEWTH68EHFADL88W5NGKTKGTSJC7JF6XR
EGHGXWDSDJ07GJJ0EQ4FFSEC2HWJNHKDDMYNYLKEXLH6F58YE00RL1CNK8JJ0QWRLXRMRS7X0NLA5366P86EN0EJ22QK91SP0SL0R4AG4G8XRJLNZHDZCS58
DDZML851H7H4YQEJFBC0TKFJ8R56SBKFZ59WCPXT6BA7A015PETYFHS94GDPN22GYGKEFDMMZ4JQDSE30MDAFX33647A7LMBTZHH1FXJCCH4X9TSR5YFYDP7
9A9DZRHKZEMJYTQDLG9RJFSK16E9GCDJF827Q610ZPDHY3T5NFMJSZQ6PCCF4JD3ZQFFSZAM00FGNZWPBRH9HGDGY47J8GTFFHSEE1BYH8HZF6G86DYP1E0T
28A123GJ1MXMP8QZE0CYMYPJENH5DCPQ6SRL2ZFF2S9WHYS9GX22F93Y91MME5LGG59SW4LHQQA0PYJ00BSALW7PXJLN07G74K968QP5MST566TMXB4RLRPY
KEYEHHHLMSHEJZ60MCT4FX0KMCS3DKDXFENBWHJY85LG2HBYTDKZNEMMKHSXMFJTYJE25XXDYYYFJC0AJLQ46EFY9LFRYD91W8FRTSDWAL4RKDAQSJZ1X79H
H4HJWSSG4LZS01CM89MHM4GH83KTF7K3Y8T2HBAL48GCNR6M94XRMA4DX8MSSGW6X0E7P04Z2D1WEBYQ10QQMGB1XK6D5E0Y113RH1H6LJE615KWTH1PDCYN
2BDY4YTKTESS581BZMXJ3C7ET3D5HL574AB56DGCSM2ZEFRT9XALQW6RMGMXT3MAXSHLXCKE6CTGN94TZN80WKGRL2FQ8TJT75QMA47TTD41MMWXM1ARAMRZ
YFFQZDRNAQ5TGJ2J2BS1KC0GE5ZGCZCDBP3R33NX75GPFAHKW8A36MRZCZCHDH9ZGLZRJZF1W6RJ9HQ00KN8GRXYZQJTG6J3Z535WPQZSQL3GDN72PKAPD7G
3Y4N78BNCG58FDXLBYJNE46NH1AGL93JWTJLN1LC477CGMKN8PP3HSBE72THGLMS80G8HT6LD2KYYNYQPN7GSXTTJ2SLRZG83BEYEZMKAZA7G6S06T7TZC25
QD7Q1Q5E66LGQ35KRH1ZN7BADXBTPMQ86AL3JB4R2XZ982B86CXAGMT1TEW17TP2BEM1H0T7SKD43H0XTAL448KAB37FEAJ4GGSCB7GWT16EJ8RAJLNMCKHZ
9R7Q61G8457YJQXARL0JCGR99NR5J4274LAR8M8G3SZ9J63JHPQ10QX48XXZB4N1XS19ZNJYK5WP4MHL508TXTSTZEKR2K1JNW6Z9Y4BM76FJN1F0NZST8BJ
B9K576F7DMN88HADR9RJTTD41TPWN6B7DTJG3DP46Q0KGM2GNXLTGSSH7J67Y2XE03TNHYHNNCJGA33S8JBXN9B77223H4JCT63SBYK7EPQ076LSYJDR7ZB2
A58PFZ3ZQGMAHG9901483XHD39DFSHKY2HRJD3LBR3WHRGA04NW1X6LQCA22QCF9T065NHKN1BH1NPZHQAJWTF1550YDNG81BKQ7MH7PYNRRPH48Q8YY998N
4PC4F6SQY4SQ5T88T8Q1FGP22NFZ99GA1YNZBKFCQLBEQSBYHAT38Y8TY9KWFLDZMDDKXNW9LLQC0AECTMHRQFTNH6W3HQYXBEDMEQRACQFRKRD78T7LHDLL
GPJDMSPSFCZY7Z7G0MZL81HE2R66DHZ1G5L2GEC7JY314FHD9DSR58LRQH6BEPD1ES0T0NDEYH5JBX2EPCLE76LSPHWDG85T4TXD1PKTZ06RAELKK6F1WY69
4DMEB3MYE1EMYFPH6AHT0W6EEMMQQ4YGWQTL6PMGY72TM9SJ3QAN19EMN7E8Y5X2DZRG0M3ZKMKZNNL38G3NJJ5JY7ZT470WPGKFW9BBRTRR1L3PTZQ6LDYM
ZHPENFPQ6J2D75H7QH7N20QGBM8J7Q0PGFD0S3X1945DXXZWBP95F19QQYZAP3K0Q26485SRXZEK6TBX1LYGMWFL27GWF5J2RYBQMWQTSC7TLFG8YHXQAKDE
11PZ8H6QJ25GZE48JYRF0Z9ZDZ005RYQTN0NHMQMYDW4S70PBGXP3Z0HD6367DKCCECT77P3Z918DH5HC1GH4BEWXJDMWXA8RFRB8P0FTH95ENXKYAMK2BB4
1H0FB5WBTWFNT6XXPHYDZ02HM1MYDSKLQE1A7XWCHJCSRPDPGL3C181QNPNCKKSHX6QM51A2AFDA2KKAFWXZ1G01FFEEDF9NMY539H8CBBXMTDHZQBDNPRYX
F5NR1TRPW5BGXSR89XDXN978EZ8CZQQBE83MR3L2K4SDPA7TEGZDKNBXGSP3J8G2LNYLBMNTFSMXYJZLECAF3WKRCJ39GNGTPZQ628CKH6D4R1XLELYJNAJ3
N9L0994TSGCD2427NPYNR0LL15F1ZDDG8ABRCY92H5Q6F92X1XXTSRQ7MW3284ALN253FP8BLL2YP40YBS9WAYEHX47959Q5ZDK73ZXJWJSTML9SG03MG4FA
MM3CR7SZGNPRZ2JSBNBSELR113PHT2PPYBBYX9H69Z6SAYY2R3CLJCMSH5QSR8J1AT9EXSZH8Q8JNQBN7GE1YB7JKAPK446PSCDEX19TRYANN2P7AD37H0YM
5RZXJQWP7QD5N8PNQSNQCQSX32MYKQQ0QFAHWDGLB177P67M16LLSGYWS8XRL9GDHMT66JZ2SN5JAJGBYT4AJJDPGNNTT0MSWPTDHJP8JEGRX3BFE1L2SAWM
SWNBK59BEXHWCG19T4WG1L6MMH1HQXXJ4218YXS5K8NWXYGGJK3S0XH9BJS6CERTNTRRGA5LJLN3MGRGL3MT8XXA3ETZA9KSXKSARZZJWG9R39GJL2AC4LX1
6ZJTKQ7C49FJK783PY37WR1P2B3BJ8CYXW9DTE4SQCP4GM108E81LNYRBCBT7Z9MSF6BYR9PCTHYNJG82XPN2RNMD7PGN12KZLT45FHAHE6Z28X4T24GXH2P
R90RXSS70BY9843F2XBH985XTX6GBE03Q73WTWE4L5ACX2N44B56S3LBQHNA9FX1MQXNTNHQ5Q7YT7KAXGTENQ480HJJ33SA6DSCD87ALX7Q51J1HAMHJFFD
3CWGHCMN4H39FF31SFNGZ44ENF9JA4229HSS1HSKLW71J09P690P6CCAN5RSX2CZ6X8BBHT46DKYYBRBE06ZRFQN1QSGT2C3J601N4SA9N72B754QHEPB3QD
6J1CBEZ1Z3WG138X3K4GTXDRBSP0W8YEGTJN851XKXAAQJE7QWQR4BYY9NGG8H1XKA2ZZF75SWHJ79YSGPAD66LD69YJS9CRC8HYWN8C909DJWFETLSCZG4P
JK4NBTNGRHHWZ08S6RXR2KL000R37J71QR169189JFGLA20TXA94YQDG2ER527Q9BRCL16QSMFGHJBSC2BTX6M6E767L2AP5A3B7FA9TLKSFTYP0C1AFQTRA
CBZ8W0DDPZZXQKGCNMN18QH6RQ5PYMKDS57C9Z4TCEG7F3GXS0GHXCNFBL2333HWSRMBZFEYY13GDNAX08BA77LX4N8130XW01LBZACHBZCHZFB3WF2E8NN0
P8H7W7LJ371YWL4LW9WAHL2A1HGNH0FZW3NGFELPZ8NA34G5ASHPG6K4NLHZTBDF0C76KHAMFSHPQDLJXWH6D8NDCZ1LR51GSC8R14MZPX0MPTG2FDENBDDG
1591BXHWQFWRCRHBXCS2LMHY69Y2MEAQ6CR2QHZ53SMHSXYEQAPC4GDNKE2GAEFTJFGZ332Z2YDDFEKWGJ8DL7CFHQ7P38Y9WZS5YCWFDQNR3K2NP1CDC4RT
ZBYRSTW3K3SSZCLT7E9FFZX24PP9ZPXC60DTHQLM60HPNW16P2M2AT4NYZ94GZTQBMRREL8Z1F014ZBZ6S697N9BGFY3BW60R6GLH29KJJA852XHC5A0LCP9
PNL69HMWNWNZHT55AHCH6PL0EG6ARSCNSS638J5MBL2RQY5K4AMRKB0XN8LR4SRPR678RJEBFNBWRK3JRAKS2LE2C62T1X11L3N59DJHDT6E7X27E39PK89W
JZ0S6Z7HR72WZ3A1ET5ZZYNTCT12RXZJ7BN2N76R2EWA4C725J6WK60W3HZC7DWXZ94GDNZPESK2XTH1APXRAA2XQ6QS01DBXWNN9GQ8M411SK9YJH1NQF25
MTZ92TL4FE44TGGPE4YF82GG7HGS3SZSBD3M5J0TXCSZY50LXZ1964KWLLCKG1RNL35B9TDH5FDMTY3GHDKN9P9PEMJ1QK1CW8G0R1C5G4C4JHEM9X502PFP
LCYD2T4ZRE3R5ZMWE662PG1168HE5BDBZEHAML8M6A84QD63098FKPZY5D53HAGDGCFWSWYTDQSB31GNX2FCW7ERZ7CDWYJKDDBF8CZBBMF0P60WLA8N93NS
0CXJDCQK46DGFTP8NX2QLKT7RR6W3A3PF8HN7GJY82H9GZY06X2HCGNCGA8NTAPZJZGY3LTARKCG5D7HP5E39EZJ0FJ4LHEFET04Q4NQ83FCH8TZC8RMBHGP
BEZJWTCQXNZ9HGY0RGR42TE8REE3W17L5NXNTQQGQFJG7S7B5BYRDLQE634DDRS6RE6T3K47EMQHHAR2WYXQMW345T9693994CXT2BDPSBZ82ZTH4ZGFSR3Z
T5QXSYHPE71PFLQEJF3BBLYY9ZG3ZRRK5PDZRT0ECZDNWBD7Q4F620BZ3PRYPYSLNX9NZJEY5KRN9E6YW5BH9FE52EMW5ZPL7TLP6ARHRYZ95P2NL5HPT9E0
Q7JDN4A6AGATM6K87MR45X4B156WPGJ8QQ78Z9K8KYRG7K22ZXB2KY5A8HC5CWCBTGAJKYGP1G035S82DS426XEK7J0G7RNCZSW56SLBMTGC7X8ZE06ZAG5W
E9KHWKTSNFZ6EW2BFAGML2XASL8YTDX8MC0RW4AW3QDJ1P1ZS4207459XD2GJ4X09SGGQ8K7LX182WXC6JR28MZT66JRN3G923J96PEZWMFHRK17QTL5P9QS
CZACY85F6049X2S4YEL1X83WRKNPBQKZ0RYGQ87K2Y7JX9M083H96Y07S0AW2RSKY65PKJ43SYTE2NY1342FKSMR54WR4CSJFZ4D5M4GQA1TATW2AHKRQZYD
E2D28F3WB18JPGEMM5PHP97SATAJJ6WXLQJCW9MLXEFW1RYQMRGL9P0CY59S4GE2X284GDX6SKD6SESZE59QLJ750EGC11QG0LEBAN21KQBNCWGJRKHLPR7X
EQPMRMW03PQXQT04YP4DTE2TX7827S4HJHFJF4EPZCW05KDM497A85B9JAG1YEJK9CP49CZSHA5XYATJ3DRAA33R2R1FEADQX3GHH3QLXDRFFTDP5DSHH2H7
2KMPR7MX0WB3MAK1FKZA71SJ5R522X5HCCZD02P7H7FF9EZ4XCPMLJ07K953ZB11Z4LBBEXYZSGSF3QSH8Z2Z3L5LBS907XBRR03LSD9YY3GD7M9KXGM89C3
L251LYC51NSMSFR6G38YFE2ENA78CG6KAHAZJX2AXC4DN400T2ZHPAWPTLMLT4FB55N01W508FYMCG9LAK2SYZK99GQFD286Y8MT1CHPP4LBEPJ0435KR07W
7AJZEZQLXL80K0FJC1TKR6LJX7QFQAJA188MF5BS7S5TL16S9CRWFL7SHAEG12MWTDRZ6BM2W3GNEQK87K1M03ZC29R0ZMJJ7J5W1CHXZCX97CY22T1SEJ2N
62FQ1CH3W2MX8XKFQD4SJJDBALWGFZ05A6E70W0JZ9CFZ2A0QP5ZHARPZWF7SAJ192C4ZN8D96RSL6LCS71Q91MSF9TLCAM5E6F969MCD8XSJRXN7GQE4LF3
QHK7QZ4CQC5K5C2664TYJZR13QM1CTEBSXX4639SZPR2B2EXB8FTX0W82RNQ9EABNL16J21591RL0LRPC0YQ625EX5CZ6FR6Y19S02G40ZX6WFSMAAETE7MN
46ZRMZ684C4DQEW2THLFB09WMQF7S97PDPP60SKDX8J9B7ZER9Y3JW501TQ7FSANJS6ERYSFPN476EZ7QHFZ6T3B26XKTLKA0YGGT57YLRBHLRRC4FYNZDB6
081HCAPN70GC9QEQQ4GC4G4M7KMN2S6KHB8TWMTE94KFZX2FBAN184QK8LK10Y6C3KLMG87JGTD3GPPNKPYZZNMXEGG1ZQEYH68CJSWE7Q4QMZQG0PC3PA8D
8XLF960S9WX734Z0GLXWW9SKTXWYEYL307Q4W2PGH2Q170ZQT6T0CBLLMPMBRB00XXD443ELM3MKNX41LAHSWWGYTWCT5R41X9SB5FJCPGWQA6RZ9H806XZ3
41LX12G0G5P9T45YERPG7N3DZ6YPLH72J4JA7LMP7L1CXQENPC0ACNF160RZ3B69YYCB5A6JZ3W67Q6ABM1GMGH4B4LGYZJ1HWCXC031N536DX9ZT2CPATZW
S9KASP7QTG95BLQ776WZL0D8S4MYSXXYZD6832LLAQBWDAEZTPDQXJ5E83548YJFEYWJXPFE88PBJCNA3CKQW03YPKJMAQ9GP0E2FRN9QQEZBEQ0KH2B383B
Z9SQYBXNQRWWY9GP1TGCMXRZ7XN0T3PKLJHM17MRBY232ABFBPNS2RM5X5TGSYLGK2W65Y6J1LT0GD6PCAK8AGCLWLT3MQNZTWQQ2SZ00K7BXW7QD1L0HK16
02X9TXK2449BQSPXNAM2CKTJAQDPH5S3RE5TN0G4DKY3NN8NSE2XX0CE24C7XXFFN1HPHCH924LW439PJGWCBMKM02KA4ELH5WZX8DZ1WWF6MHS0D3RHAKFM
3DH1K2KGH56F6PGDT60PNLFDKF9YD484W0SBKNNM569HAK9B1471X85DWPEQH3GAN52K52ZBEKY0NWPY76T5JQ678ZSFYM5R3RSS2M9NZ2FGZXZNLKLXHHM0
DZ6EXHLQH0479A6KEMLT1W1MB81YFPGRGQLA06JC8MGR48SWDG0LKZ39FEWACS6R4ZADPNJ55EMFBH4HCC221MGZ7SQ0RK0J5M2L5B73HKPYMZ461BEWKTAT
7JHBQC1E6F6HQ2103RZAHETK4MB8N2N6BH26T0Z36Q1KT9LEW906Q4YG0CXF8X5SBYTDZJ4PZKJJBXX5HAHGP4BX98KRTGAEH3KETQCJ4WQY4W8LCF9BCL8M
3HMLC4AT1YM2P63ZD649D0PJ61T08N9AZRG148WSQYCM7Z2ZP7SCQK9K6FEJX8N78G3G25MT8ANSQ2M8LH6D8D2JNPQ2MQLEXK1JGQ62KACHD60ANM0M7KG6
JXLG971Z195H1H6AP0PCMXF5TACCYR0KX1LZC8DERAWTPEBQY77QM8EP4J3JKPPCRE1DBLK3Z3E044XKR54MCW59BK794A5JGQWPC371GZHEL7FL4339MK0K
N8B4JJMEHYC1CGWYCALK9Y06DPD6JR1J8CMZWCTAD5BJ9LK5Z0GSZ7T1653YMDKEJGT7R2WERLXW7DRPRGJHPAKAH96X8YNGZTPH0TWG8HKSTSJ95QSLKNG8
98TRPTZ0XGCS4MMGFZ5ZM0SCXRSCR56WDC235ANY8360AJBLLKW3E3HNC8CDD08JRT9MBT8ALK5GQJTDQ0HCNEC3344HDGMD4R61TK78P4XM8Z65BNA7EF04
3C5HRC9JCDSL2TCWAPRJ070Q75AZT5LEA7W60GRNYSLXBSHWJ4BEYQCBEXNM1E77HLWCTW39CKHGRLKKXTXFYPQLWQBXCFM1YREW2J08PC2PSCJDLCRMBQBL
MFYAA876QHDT5YREE2DFQ2ND8R6SP2GLKHGR0Q7XGZZ9HZQW634KM42D4LFHN7WKRKK041SD4885XLNLK40GMS100Z30P5EN0XFDGLKE3Z7QW7DBRRNMAY9M
4L9ZB8ZP9B3J492TSLY34EG4L00GGJWDFGT82J5ZTSLA3RBW535XJ93XJP6EHES31BA0THB28JDAKHDG16ASDXEWWZP6TKX2TLSXWW41BWBTAZ47QN93285H
FENHF26ZRT620RFQW8RCFJG24JMBZCR7KNND2612C34N33856QA5HMY508J37K01JFL3M88AA0AXPJYAH6HEJ46EGR4QKCNPNL7MCK4XEPKDHSFETBA6YTGH
9E0BK07J120ECCRK3K2YRK59TZCWKYA7L5FF9MD21YNEERX4SJKSQ03Y3HXWJELX7YSD8FPN0PDYPNKJJPNHF34PY3TK48DYQFL5SEL5CAM3AG9N09HALLKM
K6Z8JTPHZDX8KRC2A68TQR129KDP0XLELWBFF73BG8S582YJEQ63Q3M197Y45MWBR5QF468EJ47Z7B5HAH1KYP62A77HJERK6PERS3EN1ZZ3RW16691BTB53
F4YD212LRPLA76ACP8HP1JS3760CX9HK642ES4AAHZ1NQBCMS1L6AGX9QAM93X7F35RWX6D7P57MJ0DPHAAQML1LZQQRWA0MHGY78EXM6MCRWJNANAKMGAB3
KACBTNKK0AT20ZY82EQZ28QW4HREP3J4291G96LJT8B4XC0JK253TEMAW8GTAESE87TYW8K0L9CY5NSG2GR01MMXEWZGQ815KWMB1GQ8KBPG7JBLLBTMSPTF
9K6YZ385MDKP145AYEM3XKJR968BAR83KKD5973NG9Q46EP7A5FEGDEJG31M3NAWJ0N68B6XLSBZF7NLJX6ENZ20RFWCXCM3NMF8K590GL37HB45MN65GL9Z
10YGR8RNDF8XTRAJL2Y25JRC47SEH3TKPWLP1LP5Y519M76B5B5B1MGSKM08A4ZK69J3QHKNX691WDDRZABJ4NN8MFLH398F0GQ2DH2PNZR91DWJ05QD5873
9H15AN150NYRHXNZ7RS3D39YKYE0QHN1L18G1P9HQ2HGJH3YH2D9MMWZ8P35G389EEJ5D723L7NXN3FZMNSDM31DBE6N4JTWXARP927PL6WLFJ9TJB50GQYN
C7PT1Y0070FESMC0J81CGX5E8B1X6T2FCWHE2C15RG6S2F539QHKW2FYMG044SC93188ENCL0F0SATWCYAW2E4F7EAYW1DGE5XB72ZHSBCRW52MMEYYSKG9X
8W4PQDCEPZZ9CE3GJCS08Z9B42W8S09HK6ZH0Y9FHS41014EEQYB020CHK7QLE0FG337CLMTGF27Z7HTA4E571ARA9QGQX7X8F571CCHN8RGSHJY52MJ04J5
AHWJ7ECSRST1XLKX3SAWQNXD21WKKBGYRSS7029GDX4NR82C4D8GDT1P2FJC8LEQ00A7S3QX8GXQX4BBWSNQB1MMGG6XTG94B3R9GQHXF79SREW209TP2NLA
GHE4CQMG6MKQLMW1J06K1DCH85GT59B9S46XG46QFYM1QR9ZGDMHJALZHTJ2FTGES8RM07L6BB3A3LQNALHRZWZD0LQAQXG4TZF4PYN6CB9ZJ1ZRMER2QZPX
WML1TTSXKLB45BRA098EXFETRE0P9XWGTP421Q4AC1NACN1N9NDZFKJFFE2HRHDTZ9G4YDNELPGSQHK1HX4BDMQNBTHBS9EMF10G5F1BXH9WLBX28B5BSE7W
MRLH96X43SDKL59YX02YPJX5T1J751LCT71FW03554G7XJ3JEMKC0DAHLRR7TLG24HK6394SAD9L2LFPBE3PK2ZFXH8GW2S77NW096LCGQKSCDKZS90NZBF6
RW82H4LN4NFA61F8ND43K8YNYGNHNXNR4MXL0YLCRYF6TSKTF8LEL5E5WDX9N2W41H2Z4R1JYZYWMS0941Q283R14X24JZ5SEY8WYP7T03FWW6G6BXLA8L7G
Y5DK2ARP689B5ED94RG66BWX8PLLEHT5X5G4HGHNXS71FG23ZAFQ2J914QM2GNY65ZR2CH2QQ5N2W28SE94P76RYS8X5MF4CB4QG5TFDQFPTK57XNYW6727T
1MPPTZBELA173ZZ770CTT0GTZS3PC01L9RDFHHRWPTL7KW21KQ27XSC6RHSDC6SM7FTNB9W7FN2DW45BZRX6SPWAGCFR0EM8YN8C910H1S9XPPM0NT8SY1YZ
EGHYTXP9X55ZQ9DNBMX751QL2L0XMZ773N3EX78JJDLHMAHS47TXCRY85GTC3CGZCNTQ7RZYEWJBC59T4FZTFHN1DTE45TLS18J3QLDCPWG3NJL4CWSNAS10
BMDJPSNR44CGX4R27ZM7L3RS5AKH163C4HWZQWHG6135S3RTY8C5ADZHFMEG4JY5M22EZ61P3DEW4L0J3R0XDB7RSHZP5HL37N4N6KQXXC4BKCTWDH4JYRKA
H5SXX085F05MLE1RJK3P2E9Q9AZ26MEN3F2QA1D5HA3WGD2EXMD478GE4CPRADNXNW9SX8Z7JRLEENSNMFHK9CX6426PPAWQCGJP2L6B5HH6HQ6XWPCG3P81
WJYTRXYDAKCD00ZT3DCY3FF5XN98PYNTSYXSFFFLDPARX898B5P012902029Z2XMXJHSDBGJ6MKLEBC4JBCAMMMY9YWKF4ZK8DRBZG1PW6EQ2JG31YXQQ1N1
9W6H7Q8S389Y2XNHLD0RWBCFCQ27NW39ZXSPW4J506MSL587627DMGRAX2WXHMZD1M3XF916CQTSERTG2D11Y1B0FLS3BMHDD1NR2Y6QPCJCC6Y80TNZW73J
HWTYRSED6DELLGCDY9JDDSBR77HE2YX7XZBXM4NJWDXP1PCTT9M5BKMF1FY0GF8ABALSPEPTS2Y7WEC08A9AYN59FM469G9EMQF5CXP2JR5BESQ31YX9YYF0
838RNRLMQ1CB0B5EBBH4W01FZG44Z3DFG70GB12A98WJAR22377MQC9FRA3H24BARP8BY122NHBA4MDTJZPZY1B1BGJ5DTHDYFH1Z7FZNQ84L7SXNK7R6XT2
AAYF34RCC4LYR1HRR0JLQPR84B8NMN24AXC2BCQG9DH9LLWL176N67MNXENTEWS0H2SYQ4HYWE2P6HEBNZBAPKA2XS0NC50TYL9PXSRH0XZM7TS7J99473BM
2QM8EWNLN9EX3LBPRN5BTGX6KZN1Y7PFA59GN99PF8WQCL8CLDZ8Y6JAPJ7N16XBHHPHA1ZLPAD3ZBZ9ZCY40KPXP6XRWKBAM7P430XB5N0W73GNHZH3MZQL
Q9517KBK178D89HDT38CGX3C61HJ40XZJ1JYATABCJZ76EG34XHEJ1KR9MJ6WCP7L43ZA2NXBZMXTW8SHF05GEMK7G1LRRH5JYWNX9X8S1TBXRWB0JJB2G95
B27BXNK3YB3FL8WGP0PWGT5BAFFD64A27WMM9ZSWNWJLYBTRPY2F9R83FW4M6P12TPYF06J0D81LAW5K2REGRBR054WHHX7S2BEK2XEPTME3R69X78A7A6QH
N1LHGH8AR3YX852R3N33658WDJL0YD3Y5J4950CMPDTMA0M2FS85ZLR1FLZ0SYGGKDK0Y2WRQFMK6CCMX6C6HC3BSYPY1FHARMMY1RYN08K87WD5E44991LB
TD3WR1CRP61R79NQRKLBBSJHPMA1QZDY142TKQ94K24TG4RZ8LNS0JQX0KGX5YFQJKRQH9G8ZDNCWNYYEYA1KKGLPBYE3G39LCYW5H12TY8FYPGLNM8KZ200
MG1ABD3J4CBF1CK2DZKE0L603KLRC1GA4H119DS6495S5GD632ZPRPF34YCTTYBA4D7AJTT8FP41F93PZRQZPWYZ2DKGETM2MKD07M2M57PTD7Y1BPJ6TKPG
X43NAA2HRPC7L8KBS4DFYEG3DLF5FXWKB5K2H37H7GR4XK1DQLNN6GB0TQ9Z90ML9GRSR7QBQP1WJ98ZQ8GK2NLGQQCY2A5Z858QJLAHEBA2EJACG82A8NML
8AMTTCJNAGQN9JHQ1PTMH3ADAEFJD9FB3TDLL2LGSCG39D6X3CT1Z3LQ4YZ9WBLMDSBAC48X92QFPSS72B3A0KJCA5MCJB629XDM371L5N1EK7ZCXYXSSCXY
S1ENDZW868DFK5Y059YGD4RPKXJNPCYKL5JSWBHES9YRN1GLXH9P5980HZ0GYGL5WFZG8ET4Q948XECJ95HPP0FZ3ACD8K6YEQHBCK17BKS8X2GHYDZNY7ZD
9YJYAR10PJPBQRF6SEJENL9RASR79ZG0HEGPLHFE524EX3GDJLKJ7BJ9WKE0M16FM82BFJH135M8K27PRTH7EMEQZS226EJ4B31QTKRE0MBE1FD1H939R4YZ
QF6SRRQ33N7LJ7AEK2AAJF7540N4CRLBFZ90H1N1168A2403B52115QKT8Q5QCK217SK3S6D1J0FM6QSH97KNSNT48QZ7H6CR1WC6DJ32DP10863YWWRC9TT
RYKBJYGK9SWDF74JZZSBX1GFXK0Q74YBJA0Q7BHD96K428MAFHWSJSECN1B7KCXPXA85W060M1CYN66XPED4FSTF7EM1JTY3BQSL2T11WLT9ZCZGKTDDB5RX
6MGYPZD6FXHKGKAKFBBBZNN5TC6DA7N9J0W4B8RQ9HHJMQP966LP6EL3KH4RN494C05Z2M0GX3LT7DFWG1T8M86GFRGF4K39LSSNZLG7JTCYMNXWYZY8FBXA
B6L1YJ25NC5ZK9PPF3RTBFFC7G7MP547H23438F36TR1FPYSBY7008NQ27L942D544X10SDM7EP8CRHGYNBZM701212B0PJCZCEBXSSMFKDNKTGATPQN62YL
FF4AX8F66YXLAE35FH5DKW07T26C4BKC8QX9LSDHAP2TDMPNXEDDQYGYXQDBAXZJ976PT0GBFADNHCNBJDZHEC4R7WB9HN6A79KC45G3Z13EEBHXZE79JY9Y
XLW7LDWH8B10K4N5KB7F0NS3D9LWWQ24CHRK9WMR5P00Z09MPDTXD7QKEC3QB8ZZFZMHADHH3CCRN5MQ6NWG71LZLJ04GBE7R7NAGHPRTBRTK8RYM2HJ0W51
BTSNDF6D860XPXX1MFWGW2HRHEK3P0E7HF69AWMJD93CZQEX2QAZTPHSCMW3WPF625CDN6WDCM41LWGRTMHX5EDSBQ2LMJHY01L0BNRZ5JJBBEBDBRPJLCN4
7WXKBBSPCGXZ0FD9CWLGPJ16KYRFDRCZ2P556ZE03R3NFH9NAQGKZ415JTT2TL60DH9GEQ7JQF423AW2KKC58L3Z8E6R6900SJBFZFD0S9KWCBH29TRYQC4L
JNLRTTGBAPQQ2B2DL3QSGGCKJDAZ4NW8PPQ11FDB2BA40DW14T7EJ0X8RLFJE4GDHFLKSSTDW3WJHZA5J96QJ55JB1QHSFTQSQHRJKE79Q1AF7HDENB25J3J
S6MY5NPZ8GZG5TPNYRS0BQYH837CTF9B5R33NBRMPH2LS9EAW0M528X165L3TPGPPN7QBBL6RNHA99TG4P9BMZC42ST0J4X44MGYKTBKTRYX99A61C2PYQZX
4JA88E007BG1EP6MNBTTE3CMZ8L4LWBX729KPJL6TT3M1FDY85WBSMKMDFFAJQLYTQA1ZZ3JJGGA6Y6K8ETH77FZ8S8GJCHA7PBN00JE7E02583CBLTFHZ7F
8S6G2N85QSL25XPJRFB59GALTBYSRBL8WZXHGT2FBRJJF48F3ATL8B6R0BS0002J3J5R6YF2YC3GT66E9C6RGWRB5APT59AS7HSWJEHSK157B4HLTP549MYP
HNYX97DDRFQKR0NSHLTR2K50JBD6KLA8L7TQXM3FT7D9QCB0KEGCX9N3LCF0K5WF402BW8QFKB4HG1355RSY9SDQXRXD5HRXC28MT597J6RLBCBKC9S53FE8
FR7TH7GT5XNPY9ASCA0ZFQ5P1ZLE76B86FNN8G1C6YBB2298EJFRH1PK4DXY0MQ89QMRXQD5NHJW661GYGG4408MYT8GN529DY9NNMLNXXW55X9M6K0REWML
0E906Z4G4604JTHCGASWL3RH9GHQKFCLRMT72SQBTXYAG4X3ND5E4M9AWAJ3LWKLY6HFTG82APD8ZNAZDHT9XFC4LWT6G6HH9LBXTA43KW6MWT28S0YEADWG
8FQX507N1F2WLBMFL4TNA0MY7QL93PK6XHL134YE3FQJ8LDC253D9ALDJ689AHCLLKMJQQX7W91YPLD9B48EPCS4FHA5EX7ADJR3ZXFAHZ6G9GKLDHACTY9Z
J32XQLL3FLZBYBWJNELTT08MCJALSCWPHZW0WPN4EG15PFFQR1Y9ZPNY4SMQ2RM7ZY8AR8NEXY62GM9GQ2J09JZ6P2KW2ZE6H3TH61WL9ENJB9LAGFMJC2R3
HSM7MBWFG0B8Z5CS6CNX5EE9YB7E5B4290LNQNFXF89BD5WGS9AZQGKPPTCED9NM4LMGLT6YRCCDAM11R74QQQMMR6BDCB7TJJ4XAR06CN0FMHGMNDLQ3BL3
244Q1RRSJJDRJY6WR848FEEFSAWW2PM4HFGJFZPDLCDFDGJEXMBKB4E9NQ0WM96XHQC3FBHYZ9G1JEM37KWFQQH8M3YC5BPX6B85008B42D8P6661E5NC28A
8PFC7XPWMMNW9M561AKR9172M42ABTTSYZXB625JYLB23JLDR0E9ZZZ2L6D43MPD2WHLH2F4N8B3Z44HCWS1S8F0JRD9M3M1MX8X4934Y0T4DSL43GK22EK3
E2LB83CD86PGD7ELQM2BZNYY0AXS0DW1PNC5B4AARS8E9BAYSD38DDPR0WA7QC3M3H3G5X8MDE7M498P8657GEB7PW2M88D60H8TBENZA62PDT87CD2RSM7K
Y5GQC1SRK49T2TJD333HE6RKG2K5110TQBYBCABWPEF9MDMAHAPW9PBJWTFX26Z92HEQPHT8WZYRTQ6KKS37JG17JGMBQ2ET7AY6Q9K1SST7J3TQ2LS3T7FA
Q92MZNPNSRAJ9RSBSBFPGZ8S942FRJLP7NLPPDQJDJ88J4SN1EPH95Q0XTAX6GW2GP9FC1DJABGNYMN9BQALEK1N7BX2N4H0HSAT70A9Q89NETT4YB2LT0LD
QYQ8K0WM5DP4RSAQ438WER65BLBE1JKRK48NC15S5LQMGKLD9CQQCSDZ0H9H5BHGY00DH9F2Q21X3MNLA8CXHJ6C5C13ZJNHEF994HZG646XFAYLJERM27AY
G7D31C15N2NH6GDWD0HRM4LGW5WHJCJEGWJ4MSXMCXF0ANJ00M0WFTKJPBMHG0W3J71G9G25AJTT464D4D8C5Z9LNRWG0GNEP1T1H0QPDRYW7J8RWLCR9ML9
MPJW0JPFAYGQZXM1FQFMZW8AP6ATJZXGGCS7WJ8CK2B5JKQD426EZKGG1M4S23MAHZWALYF0X89CZY4LXRA7K8PADH1PYQKJFBN1CXZPTKBKA1FL15H6CG9J
CX4E9Q2462SFJM26Z518QZ3R216Q4DZ9PLQTCTSFGRAE8DZX649KL5KNQ18GLFJDYHMDKZR5HA700PAMHCZWS5W4QYEC0XJZ701T3TQ13AZPSQ587W1TRXAQ
00B7STP9HYX19W95CSE2J7TYEWWL4CCCRZGAJF3M08QJ11FE7YNYB114FX23PQHPFCSDPW6F1KC96PQWN8XBP0Y2AF8SWFKJNNWQWDWXX8Z550N5WXKTSSG1
L27NK1JJY7H2Z6J7J540T4TF36HWTCFQ8M8ZPG5RP2XGE3PXKHS757MK2X3GPPX5L92DKYEJXNERBDX99DDPZ6J65T8EY97MF57EMZ21GNTNCGT515LJ4XRF
ZQRHYBFGJM102FHAXKSP57L8J6G85Z9WFW60L5BX3CA8PQ424P9X6P9RSJ8Z5YS121HRQRX7CLK3CQM9MRZ4PKJKPG5EHZ56JX6ZX5PQ746GZJJ2J5B4018F
4CRLYMHK1NM0LLX4X5E60XLEY4WZSY7QEBD5S9G6TW8QP9G2NTEFGARHNWEKXH4NRW5A099HGRGFW3J9EX4TL4KE7FFMBCM3M32W5G69Z8HAAMCGTT48AQ3W
WPR53MKASCCZR0KH28QEE71MDSEFJJ4B44K10PB5BZW3BBY5SN7F739G2Q63HRGLJL8DDSAHMC1SZ9ZMTP9DB0YM2BS6B5HT7F5ZLZD315W4PS0YK4ETC5PQ
KJST082QWYFPHR7K5E8E0W5E77MGJ5XQ0DY6BFKL0EDCLGW7TYH4XXW5MY90MH29TFDES2ALNFKE7BHDCE9QRGDMZ8W2FHZHB19903QFP4YAY40EW432D4Y1
020A63Q2E1ZYD0G3CZXEESSPQ29372EQ41KRD4XSK6A2M886XTGR7MNAY9X3ZN7E11LLKRGP7MWJEYYRWZREXRGF9APKAAF7BY49YDTB7FTZGWZKSHFEH7YA
WNRCA0GWGD0ZKHR139JR05K52E67Z79S7W29JAGEFB9EYWMC00C7924ZPRP07T52FYZD63KMGB7ZLK1TLECQN9FQ9XXDG1P7TGG3NLA0J93X7DGHSYGKW640
53Y9DNE5G1S08Y7G20ALR82SQAR194HPT4T54MBDQ9YL23THP3WN6HKKQS61PPRYHTMLPSA8B6C5X5X2K4W1L3ADW9LTT59KPR0QS5LW4KR3GXM5BR8JCJ2M
6RN359WE9J330K5CNMZ012TDXNWQJZLRHEC28MNYG3C7WA09HBJKM6216HRRDLR96DGK7GSQRXSWL5GWJEBGGMH8X48N2QTEBJXFEEWENCPESFPNR70GK3XC
1T30YL1CX0PR1TEY5D5GNL0N46G8F67Z303D1D40RZ01K190LN7ELQE2STTE3C9ZNL1YSKZAMB7ZDEBAEFGAW1E32MJ866XAZG7M5PH37RRZL38AQ2TT08EK
E2ZGL789QKRDJ44YJ44XT0LRKQTLL80ATB88FEYWFY8CK0EQDWW5KNMN8NECMYFPC95PQ8TWPR84GBZ15AQ0SE0HDYT6TZALKHDHFM628JB2TN0QS2XRNA4C
6A038MKHXXLKTZCN5G2Q0KM53TM3BFZ2L38CSH1E7MXCXPCYF8P6HLH18XDASTDGEBR0R1YBP0ZWXYXSPW1GNJH83942XWSN5S0M7ZK6BME8QNHM9J29Q7R7
DJLP7GP45FFJ7EYF7L9SEAHQR7YQ21SPSGQ9BMAK95PD6NN2E9DACTJCDSBP7989KLFT357LBAAXSA40C9MHFGTN5DQJ856ZB5K031T0BBSW2QJ8100GBH0J
TXQNYKZCG5NNQT0AE5CPDWM5K99TSJ673K7DNP40L40DA6F0HNF99TPBFTDEAGHXLRNENH1E5ZSZPE3ZEQSQYKA06FY97JZHJGW4DX267CP6K7QXAJ96QCMK
NQ76HNP5G9F1ZKBN26A32KQDQ10MEWGDTP0KPFG0H7ZETRD060NEL8KDNL6DPQK18EAMR89HSK97K8812MMNEGDE90KTXPWTDDPE7TW97GD12BW4SRHFZTYK
4S6BKDEXJDN159X4KPS363113KZR7HSRQ1345JB0WBZRL74FAPP97YX0S7G9R5WQQ5ZD0RRJBHJJN8CA5B3JCQDXS1ZSQSJ5ZLABL6TJPA8PJ7KFFWCP1JW6
AATJ1A0JZMTW427HESG2T5KHN2GY3SB2YE3T0PABLBLXM477PP51EGJDSELWFJHG2Y4SRNW5BNYS439ZBP4H0M9LBPNQHZX7439J3000FZL3RPRBQJY20WNP
9NMAG19EDFXH6SC5MSS8JAAZN7A0EAXQS1JMW6BQM8G4HRFFALWRGDH0GP7TK5RRQJMTYSRQHZZLZ71QHXQDNGYGFB4DKJE4QBSP9A8A8XGFMHWFBTN74RQ3
BZ4952AD5YCN76SK55D3XQQMYQKJQH96E6C7EXG60YA8BYHJ8TTTYYP1P4S853K7XW2R4A7D79BX2R3T9L2WMWX57ECJJ21CZ0CLM0SJJ9339ZE0WNJRWCML
K6FQEZ7TB0QK74QY67DDPSR9SK93WNKBAD6GTWJDKZXTLWWZE8TDH9M87N6LNYJALRM820BC3MLJRP5EMPSX1Z9CZCKZEQ8APEJW1TMDP32PBRDKPFDY7MDP
A2G09XM1NGF14T5HLRZJYTQRZBYPSEQYNXZ9JXXJ6KAN7ZHHHD545E4W4K9QFYNX0GZE66291E93TS1KXQQ13ZX15ZR14NGCGCJ803YZ42YACTYPRPRCGFL9
7LXRL6DT0WR7M1NFFR5N9MPW5NM9XB0MJA8XT7Z4LZRBJNQCNDC97B047W6MLMDFH7SS2MQRXQB3QAHQ01Y8DGGZS820DP69ATBEP6E2SH84P0GFZH9TDKJX
Z3AT79XWX8QWJ3HN1FHYAD0Q9JYMQKT0WBHLE7PNKMWPA6Q7MG341NG24ZYZM9BYLR56BEA9BDTW0RL9ZJA4487MF329LMW78NWSDD3ZJEAEEGQ9EJAWZ9Y7
5XDBTQXN8HSBTWR6QAGCMF5BDHL1AMCZ1JNWZ2WX738D58GD43J0QFQBBDCW64A3MSMXDRKG3Z1SXHF3YCNNP877BGL2DGM7MC56D0QH3RQQHW6JMA45JYJ1
ZF0T79CJMAY65NNKN8HHYN5QAFDR0213JZEYSJSHRAP5SPGSMD8QDC5FN600EHPXEDFF2EB2G7R5AZG4TXG5P9TQDH2SEWDS8NL1EJF1Y0Q73FREKG64K3Z2
16CMD4ZB4WC27N0BF2FPKFCP7FH1PD0Y0R8YJF3M4H1RZZ3B6Q6Y2FRLAH8S92AJ2JKBC9STS698QLSSCTAQHACQP7J1LRSNJRBW99F3C4AB219QWC5AXAK2
9PFPPLPHL66PC5B4F2YJF2ZJ82R9DKG8475H4LLXZZ2Y25KJRA80DFDBKDL2X9T3EQYWD541W79ZJ7LJWCEQNKAJXZ3KKEWXC244Y5KXJB78X4G4TTGT78AM
X2GDNKP3CH1E26NB26AJN8MTNWX61L40RERNXNBPZDC603XSFENFBMH7Y72TH7HHZ4Z7HRAEEXGYQ66WGA4NHL744LSL4Q2EG6HC7QKZWL41HNFDA3CPKGHQ
TQDS5Q09S3MT7GJE9MMS5TFHLPGFFCWH78QJ526G8J4SL8FPD9J6XCPR300PNKG7ZFY2KBAZTP885W56009SRTSCHLBHFNNBXYMCCH1LZPC29AE45QQMRJA4


]] , '%s+', '')


--*****************************************************************************

function preload_1010()

-- port from golang: https://golang.org/src/time/time.go
--

local secondsPerMinute = 60
local secondsPerHour   = 60 * 60
local secondsPerDay    = 24 * secondsPerHour
local secondsPerWeek   = 7 * secondsPerDay
local daysPer400Years  = 365*400 + 97
local daysPer100Years  = 365*100 + 24
local daysPer4Years    = 365*4 + 1

local unixBase = (1969*365 + 1969//4 - 1969//100 + 1969//400) * secondsPerDay

-- daysBefore[m] counts the number of days in a non-leap year
-- before month m begins. There is an entry for m=12, counting
-- the number of days before January of next year (365).
local daysBefore = {
                0,
                31,
                31 + 28,
                31 + 28 + 31,
                31 + 28 + 31 + 30,
                31 + 28 + 31 + 30 + 31,
                31 + 28 + 31 + 30 + 31 + 30,
                31 + 28 + 31 + 30 + 31 + 30 + 31,
                31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
                31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
                31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
                31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
                31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
}

local January   = 1
local February  = 2
local March     = 3
local April     = 4
local May       = 5
local June      = 6
local July      = 7
local August    = 8
local September = 9
local October   = 10
local November  = 11
local December  = 12

local Sunday    = 1
local Monday    = 2
local Tuesday   = 3
local Wednesday = 4
local Thursday  = 5
local Friday    = 6
local Saturday  = 7

-- absTimestamp returns the absolute second from {year=1, month=1, day=1, hour=0, mintue=0, second=0}
local function absTimestamp(ts)
    return ts + unixBase
end

local function isLeap(year)
    return year%4 == 0 and (year%100 ~= 0 or year%400 == 0)
end

local function absWeekday(abs)
    -- January 1 of the absolute year, like January 1 of 2001, was a Monday.
    local sec = (abs+1*secondsPerDay) % secondsPerWeek
    return (sec // secondsPerDay) + 1
end

-- convert timestamp to year, month, day, yday
local function absDate(abs)
    local day = abs // secondsPerDay

    local n, y
    -- Account for 400 year cycles.
    n = day // daysPer400Years
    y = 400 * n
    day = day - daysPer400Years * n

    -- Cut off 100-year cycles.
    -- The last cycle has one extra leap year, so on the last day
    -- of that year, day / daysPer100Years will be 4 instead of 3.
    -- Cut it back down to 3 by subtracting n>>2.
    n = day // daysPer100Years
    n = n - (n >> 2)
    y = y + 100 *n
    day = day - daysPer100Years * n

    -- Cut off 4-year cycles.
    -- The last cycle has a missing leap year, which does not
    -- affect the computation.
    n = day // daysPer4Years
    y = y + 4 * n
    day = day - daysPer4Years * n

    -- Cut off years within a 4-year cycle.
    -- The last year is a leap year, so on the last day of that year,
    -- day / 365 will be 4 instead of 3. Cut it back down to 3
    -- by subtracting n>>2.
    n = day // 365
    n = n - (n >> 2)
    y = y + n
    day = day - 365 * n

    local year = y + 1
    local yday = day + 1

    if isLeap(year) then
        -- Leap year
        if day > 31+29-1 then
            -- After leap day; pretend it wasn't there.
            day = day - 1
        elseif day == 31+29-1 then
            return year, February, 29, yday
        end
    end

    -- Estimate month on assumption that every month has 31 days.
    -- The estimate may be too low by at most one month, so adjust.
    local month = (day // 31) + 1
    local monthDayEnd = daysBefore[month + 1]
    local monthDayBegin

    if day >= monthDayEnd then
        month = month + 1
        monthDayBegin = monthDayEnd
    else
        monthDayBegin = daysBefore[month]
    end

    day = day - monthDayBegin + 1
    return year, month, day, yday
end

local M = {}

M.TZ = 8
function M.localdate(sec, tz, t)
    local tz = tz or M.TZ
    return M.date(sec + tz*secondsPerHour, t)
end

 function M.date(sec, t)
    local abs = absTimestamp(sec)
    local t = t or {}
    t.year, t.month, t.day, t.yday = absDate(abs)
    t.wday = absWeekday(abs)

    local seconds = abs % secondsPerDay
    t.hour = seconds // secondsPerHour
    seconds = seconds - (t.hour * secondsPerHour)
    t.min = seconds // secondsPerMinute
    t.sec = seconds - (t.min * secondsPerMinute)
    return t
end

function M.date_reverse (year,month,day,hour,min,sec)   -- cgabriel 2021
    local days             = (year-1970)*365 + math.floor((year-1968)/4)
    leap_year_days_off     = 1 + math.floor((year-1900)/100) - math.floor((year-1600)/400)  -- cgabriel 16.7.22
    leap_year_before_march = 0

    if month < 3 then      --  cgabriel  3.2.24
        if (4*math.floor((year-1968)/4) - (year-1968)) == 0 then   --  leap year
            leap_year_before_march = 1
            if (100*math.floor((year-1900)/100) - (year-1900)) == 0 then   --  no leap year
                leap_year_before_march = 1
                if (400*math.floor((year-1600)/400) - (year-1400)) == 0 then   --  still leap year
                    leap_year_before_march = 1
                end
            end
        end
    end
    leap_year_days_off = leap_year_days_off + leap_year_before_march

    days               = days - leap_year_days_off
    days               = days + daysBefore[month] + day
    local secs         = 86400*days + hour*3600 + min*60 + sec
    return secs
end

function M.date_format (format_string,sec)
    local t = M.date(sec)
    format_string = string.gsub(format_string,'%%y',string.format('%04u',tonumber(t.year)))
    format_string = string.gsub(format_string,'%%m',string.format('%02u',tonumber(t.month)))
    format_string = string.gsub(format_string,'%%d',string.format('%02u',tonumber(t.day)))
    format_string = string.gsub(format_string,'%%H',string.format('%02u',tonumber(t.hour)))
    format_string = string.gsub(format_string,'%%M',string.format('%02u',tonumber(t.min)))
    format_string = string.gsub(format_string,'%%S',string.format('%02u',tonumber(t.sec)))
    return format_string
end

function M.date_format1 (format_string,sec)
    format_string = string.gsub(format_string,'%%y',string.sub(sec,1,4))
    format_string = string.gsub(format_string,'%%m',string.sub(sec,5,6))
    format_string = string.gsub(format_string,'%%d',string.sub(sec,7,8))
    format_string = string.gsub(format_string,'%%H',string.sub(sec,9,10))
    format_string = string.gsub(format_string,'%%M',string.sub(sec,11,12))
    format_string = string.gsub(format_string,'%%S',string.sub(sec,13,14))
    return format_string
end

local tmp = {}

function M.format(sec, utc)
    if utc then
        M.date(sec, tmp)
    else
        M.localdate(sec, nil, tmp)
    end

    return string.format('%4d-%02d-%02d %02d:%02d:%02d',
        tmp.year, tmp.month, tmp.day, tmp.hour, tmp.min, tmp.sec)
end

-- test functions
--
function M.run_random_test()
    for i=1, 100 do
        local ts = math.random(0, 2^31)
        local t1 = os.date('!*t', ts)
        local t2 = M.date(ts)

        for k,v in pairs(t1) do
            if k ~= 'isdst' then
                assert(v == t2[k], k)
            end
        end
    end
end

function M.run_full_test()
    local total = 2^31
    for i=1, total do
        local t1 = os.date('!*t', i)
        local t2 = M.date(i, tmp)

        for k,v in pairs(t1) do
            if k ~= 'isdst' then
                assert(v == t2[k], k)
            end
        end

        if i % 100000 == 0 then
            print('------------------------:', total, i)
        end
    end
end

function M.run_profile_test(times)
    local times = times and times > 0 or 1000000

    local from = math.random(2^31 - times)
    local to = from + times

    local t1 = os.clock()
    for ts = from, to do
        os.date('!*t', ts)
    end

    local t = {}
    local t2 = os.clock()
    for ts = from, to do
        M.date(ts, t)
    end
    local t3 = os.clock()

    print(string.format('%d times call %s cost %s seconds', times, 'os.date', t2-t1))
    print(string.format('%d times call %s cost %s seconds', times, 'M.date', t3-t2))
end

return M

end

date1 = preload_1010()

--************************************************************************


function preload_1005()

------------------------------------------------------------
-- rc4 encryption / decryption

local byte, char, concat = string.byte, string.char, table.concat


local function keysched(key)
    -- key must be a 16-byte string
    assert(#key == 16)
    local s = {}
    local j,ii,jj
    for i = 0, 255 do s[i+1] = i end
    j = 0
    for i = 0, 255 do
        ii = i+1
        j = (j + s[ii] + byte(key, (i % 16) + 1)) & 0xff
        jj = j+1
        s[ii], s[jj] = s[jj], s[ii]
    end
    return s
end

local function step(s, i, j)
    i = (i + 1) & 0xff
    local ii = i + 1
    j = (j + s[ii]) & 0xff
    local jj = j + 1
    s[ii], s[jj] = s[jj], s[ii]
    local k = s[ ((s[ii] + s[jj]) & 0xff) + 1 ]
    return s, i, j, k
end

local function rc4raw(key, plain)
    -- raw encryption
    -- key must be a 16-byte string
    local s = keysched(key)
    local i, j = 0, 0
    local k
    local t = {}
    for n = 1, #plain do
        s, i, j, k = step(s, i, j)
        t[n] = char(byte(plain, n) ~ k)
    end
    return concat(t)
end

local function rc4(key, plain, drop)
    -- encrypt 'plain', return encrypted text
    -- key must be a 16-byte string
    -- optional drop (default = 256): ignore first 'drop' iterations
    drop = drop or 256
    local s = keysched(key)
    local i, j = 0, 0
    local k
    local t = {}
    -- run and ignore 'drop' iterations
    for _ = 1, drop do
        s, i, j = step(s, i, j)
    end
    -- now start to encrypt
    for n = 1, #plain do
        s, i, j, k = step(s, i, j)
        t[n] = char(byte(plain, n) ~ k)
    end
    return concat(t)
end

return     { -- module
    rc4raw = rc4raw,
    rc4 = rc4,
    encrypt = rc4,
    decrypt = rc4,
    }

end

rc4 = preload_1005()

--*****************************************************************************
        
function preload_1006()


local spack, sunpack = string.pack, string.unpack

------------------------------------------------------------------------

local function FF(a, b, c, d, x, s, ac)
    a = (a + ((b & c) | ((~b) & d)) + x + ac) & 0xffffffff
    a = ((a << s) | (a >> (32-s))) & 0xffffffff
    a = (a + b) & 0xffffffff
    return a
end

local function GG(a, b, c, d, x, s, ac)
    a = (a + ((b & d) | c & (~d) ) + x + ac) & 0xffffffff
    a = ((a << s) | (a >> (32-s))) & 0xffffffff
    a = (a + b) & 0xffffffff
    return a
end

local function HH(a, b, c, d, x, s, ac)
    a = (a + ((b ~ c ~ d)) + x + ac) & 0xffffffff
    a = ((a << s) | (a >> (32-s))) & 0xffffffff
    a = (a + b) & 0xffffffff
    return a
end

local function II(a, b, c, d, x, s, ac)
    a = (a + (c ~ (b | ~d)) + x + ac) & 0xffffffff
    a = ((a << s) | (a >> (32-s))) & 0xffffffff
    a = (a + b) & 0xffffffff
    return a
end

local function transform(state, input, i, t)
    -- process the 64-byte input block in string 'input' at offset 'i'
    -- t is a uint32[16] array. It is passed as a parameter
    -- for performance reasons
    --
    local a, b, c, d = state[1], state[2], state[3], state[4]

    -- load array
    for j = 1, 16 do
        t[j] = sunpack('<I4', input, i)
        i = i + 4
    end

    -- Round 1
    a = FF (a, b, c, d, t[ 1], 7, 0xd76aa478)
    d = FF (d, a, b, c, t[ 2], 12, 0xe8c7b756)
    c = FF (c, d, a, b, t[ 3], 17, 0x242070db)
    b = FF (b, c, d, a, t[ 4], 22, 0xc1bdceee)
    a = FF (a, b, c, d, t[ 5], 7, 0xf57c0faf)
    d = FF (d, a, b, c, t[ 6], 12, 0x4787c62a)
    c = FF (c, d, a, b, t[ 7], 17, 0xa8304613)
    b = FF (b, c, d, a, t[ 8], 22, 0xfd469501)
    a = FF (a, b, c, d, t[ 9], 7, 0x698098d8)
    d = FF (d, a, b, c, t[10], 12, 0x8b44f7af)
    c = FF (c, d, a, b, t[11], 17, 0xffff5bb1)
    b = FF (b, c, d, a, t[12], 22, 0x895cd7be)
    a = FF (a, b, c, d, t[13], 7, 0x6b901122)
    d = FF (d, a, b, c, t[14], 12, 0xfd987193)
    c = FF (c, d, a, b, t[15], 17, 0xa679438e)
    b = FF (b, c, d, a, t[16], 22, 0x49b40821)

    -- Round 2
    a = GG (a, b, c, d, t[ 2], 5, 0xf61e2562)
    d = GG (d, a, b, c, t[ 7], 9, 0xc040b340)
    c = GG (c, d, a, b, t[12], 14, 0x265e5a51)
    b = GG (b, c, d, a, t[ 1], 20, 0xe9b6c7aa)
    a = GG (a, b, c, d, t[ 6], 5, 0xd62f105d)
    d = GG (d, a, b, c, t[11], 9,  0x2441453)
    c = GG (c, d, a, b, t[16], 14, 0xd8a1e681)
    b = GG (b, c, d, a, t[ 5], 20, 0xe7d3fbc8)
    a = GG (a, b, c, d, t[10], 5, 0x21e1cde6)
    d = GG (d, a, b, c, t[15], 9, 0xc33707d6)
    c = GG (c, d, a, b, t[ 4], 14, 0xf4d50d87)
    b = GG (b, c, d, a, t[ 9], 20, 0x455a14ed)
    a = GG (a, b, c, d, t[14], 5, 0xa9e3e905)
    d = GG (d, a, b, c, t[ 3], 9, 0xfcefa3f8)
    c = GG (c, d, a, b, t[ 8], 14, 0x676f02d9)
    b = GG (b, c, d, a, t[13], 20, 0x8d2a4c8a)

    -- Round 3
    a = HH (a, b, c, d, t[ 6], 4, 0xfffa3942)
    d = HH (d, a, b, c, t[ 9], 11, 0x8771f681)
    c = HH (c, d, a, b, t[12], 16, 0x6d9d6122)
    b = HH (b, c, d, a, t[15], 23, 0xfde5380c)
    a = HH (a, b, c, d, t[ 2], 4, 0xa4beea44)
    d = HH (d, a, b, c, t[ 5], 11, 0x4bdecfa9)
    c = HH (c, d, a, b, t[ 8], 16, 0xf6bb4b60)
    b = HH (b, c, d, a, t[11], 23, 0xbebfbc70)
    a = HH (a, b, c, d, t[14], 4, 0x289b7ec6)
    d = HH (d, a, b, c, t[ 1], 11, 0xeaa127fa)
    c = HH (c, d, a, b, t[ 4], 16, 0xd4ef3085)
    b = HH (b, c, d, a, t[ 7], 23,  0x4881d05)
    a = HH (a, b, c, d, t[10], 4, 0xd9d4d039)
    d = HH (d, a, b, c, t[13], 11, 0xe6db99e5)
    c = HH (c, d, a, b, t[16], 16, 0x1fa27cf8)
    b = HH (b, c, d, a, t[ 3], 23, 0xc4ac5665)

    -- Round 4
    a = II (a, b, c, d, t[ 1], 6, 0xf4292244)
    d = II (d, a, b, c, t[ 8], 10, 0x432aff97)
    c = II (c, d, a, b, t[15], 15, 0xab9423a7)
    b = II (b, c, d, a, t[ 6], 21, 0xfc93a039)
    a = II (a, b, c, d, t[13], 6, 0x655b59c3)
    d = II (d, a, b, c, t[ 4], 10, 0x8f0ccc92)
    c = II (c, d, a, b, t[11], 15, 0xffeff47d)
    b = II (b, c, d, a, t[ 2], 21, 0x85845dd1)
    a = II (a, b, c, d, t[ 9], 6, 0x6fa87e4f)
    d = II (d, a, b, c, t[16], 10, 0xfe2ce6e0)
    c = II (c, d, a, b, t[ 7], 15, 0xa3014314)
    b = II (b, c, d, a, t[14], 21, 0x4e0811a1)
    a = II (a, b, c, d, t[ 5], 6, 0xf7537e82)
    d = II (d, a, b, c, t[12], 10, 0xbd3af235)
    c = II (c, d, a, b, t[ 3], 15, 0x2ad7d2bb)
    b = II (b, c, d, a, t[10], 21, 0xeb86d391)

    state[1] = (state[1] + a) & 0xffffffff
    state[2] = (state[2] + b) & 0xffffffff
    state[3] = (state[3] + c) & 0xffffffff
    state[4] = (state[4] + d) & 0xffffffff
end --transform()

local function md5(input)
    -- initialize state
    local state = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 }
    local inputlen = #input
    local inputbits = inputlen * 8 -- input length in bits
    local r = inputlen -- number of unprocessed bytes
    local i = 1 -- index in input string
    local ibt = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} -- input block uint32[16]
    -- process as many 64-byte blocks as possible
    while r >= 64 do
        -- process block
        transform(state, input, i, ibt)
        i = i + 64 -- update input index
        r = r - 64 -- update number of unprocessed bytes
    end
    -- finalize.  must append to input a mandatory 0x80 byte, some
    --  padding, and the input bit-length ('inputbits')
    local lastblock -- the rest of input .. some padding .. inputbits
    local padlen -- padding length in bytes
    if r < 56 then padlen = 55 - r else padlen = 119 - r end
    lastblock = input:sub(i) -- remaining input
        .. '\x80' .. ('\0'):rep(padlen)  --padding
        .. spack('<I8', inputbits) -- length in bits
    assert(#lastblock == 64 or #lastblock == 128)
    transform(state, lastblock, 1, ibt)
    if #lastblock == 128 then
        transform(state, lastblock, 65, ibt)
    end
    -- return the digest
    local digest = spack('<I4I4I4I4', state[1], state[2], state[3], state[4])
    return digest
end --md5()


--~ print(bin.stohex(md5'abc'))
--~ print(bin.stohex(md5''))

return {  -- md5 module
    hash = md5,
}

end

md5lib = preload_1006()

--*****************************************************************************

function preload_1008()



local byte, char, concat = string.byte, string.char, table.concat

local b10chars = '0123456789'
b10chars       = '1375629804'

local function encode(s)
    if #s==0 then return string.sub(b10chars,1,1) end
    local q, b
    local et = {}
    local zn = 0     -- number of leading zero bytes in s
    -- assume s is a large, little-endian binary number
    -- with base256 digits (each byte is a 'digit')
    local nt = {} -- number to divide in base 256, big endian
    local dt = {} -- result of nt // 10, in base 256
    local more = true  -- used to count leading zero bytes
    for i = 1, #s do
        b = byte(s, i)
--        if more and b == 0 then
--            zn = zn + 1
--        else
--           more = false
--        end
        nt[i] = b
    end
--    if #s == zn then --take care of strings empty or with only nul bytes
--        return string.rep('1', zn)
--    end
    more = true
    while more do
        local r = 0
        more = false
        for i = 1, #nt do
            b = nt[i] + (256 * r)
            q = b // 10
            -- if q is not null at least once, we are good
            -- for another division by 10
            more = more or q > 0
            r = b % 10
            dt[i] = q
        end
        -- r is the next base10 digit. insert it before previous ones
        -- to get a big-endian base10 number
        table.insert(et, 1, char(byte(b10chars, r+1)))
        -- now copy dt into nt before another round of division by 10
        nt = {}
        for i = 1, #dt do nt[i] = dt[i] end
        dt = {}
    end--while
    -- don't forget the leading zeros ('1' is digit 0 in bitcoin base10 alphabet)
    return string.rep('1', zn) .. concat(et)
end --encode()

-- inverse base10 map, used by b10decode:  b10charmap maps characters in
-- base10 alphabet to their _offset_ in b10chars (0-based, not 1-based...)
--  eg.    for digit '1' b64charmap[65] == 0  and for 'z', b64charmap[122] == 57
--
local b10charmap = {};
for i = 1, 10 do b10charmap[byte(b10chars, i)] = i - 1  end

local function decode(s)
    if s==string.sub(b10chars,1,1) then return('') end
    -- reject invalid encoded strings
    if string.find(s, '[^'..b10chars..']') then
        return nil, 'invalid char'
    end
    -- process leading zeros - count and remove them
    local zn -- number of leading zeros (base10 digits '1')
--    zn = #(string.match(s, '^(1+)') or '')
--    s = string.gsub(s, '^(1+)', '')
    -- special case: the string is empty or contains only null bytes
--    if s == '' then
--        return string.rep('\x00', zn)
--    end
    --
    -- process significant digits
    local dn -- decoded number as an array of bytes (little-endian)
    local d -- base10 digit, as an integer
    local b -- a byte in dn
    local m -- a byte multiplied by 10 (used for product)
    local carry
    dn = { b10charmap[byte(s, 1)] } --init with most significant digit
    for i = 2, #s do --repeat until no more digits
        -- multiply dn by 10, then add next digit
        d = b10charmap[byte(s, i)] -- next digit
        carry = 0
        -- multiply dn by 10
        for j = 1, #dn do
            b = dn[j]
            m = b * 10 + carry
            b = m & 0xff
            carry = m >> 8
            dn[j] = b
        end
        if carry > 0 then dn[#dn + 1] = carry end
        -- add next digit to dn
        carry = d
        for j = 1, #dn do
            b = dn[j] + carry
            carry = b >> 8
            dn[j] = b & 0xff
        end
        if carry > 0 then dn[#dn + 1] = carry end
    end
    -- now dn contains the decoded number (little endian)
    -- must add leading zeros and reverse dn to build binary string
    local ben = {} -- big-endian number as array of chars
    local ln = #dn
    for i = 1, ln do
        ben[i] = char(dn[ln-i+1])
    end
    return concat(ben)
--    return string.rep('\x00', zn) .. concat(ben)
end --decode()


return { -- base10 module
    encode = encode,
    decode = decode,
    }

end

base10 = preload_1008()

--*****************************************************************************

function preload_1008a()


local byte, char, concat = string.byte, string.char, table.concat

local b27chars = 'ABCDE'..'FGHJK'..'LMNPQ'..'RSTWX'..'Y'..'345679'

local function encode(s)   --  encode byte array to 27 chars string
        if #s==0 then return string.sub(b27chars,1,1) end
    local q, b
    local et = {}
    local zn = 0     -- number of leading zero bytes in s
    -- assume s is a large, little-endian binary number
    -- with base256 digits (each byte is a 'digit')
    local nt = {} -- number to divide in base 256, big endian
    local dt = {} -- result of nt // 27, in base 256
    local more = true  -- used to count leading zero bytes
    for i = 1, #s do
        b = byte(s, i)
--        if more and b == 0 then
--            zn = zn + 1
--        else
--            more = false
--        end
        nt[i] = b
    end
--    if #s == zn then --take care of strings empty or with only nul bytes
--        return string.rep('1', zn)
--    end
    more = true
    while more do
        local r = 0
        more = false
        for i = 1, #nt do
            b = nt[i] + (256 * r)
            q = b // 27
            -- if q is not null at least once, we are good
            -- for another division by 27
            more = more or q > 0
            r = b % 27
            dt[i] = q
        end
        -- r is the next base27 digit. insert it before previous ones
        -- to get a big-endian base27 number
        table.insert(et, 1, char(byte(b27chars, r+1)))
        -- now copy dt into nt before another round of division by 27
        nt = {}
        for i = 1, #dt do nt[i] = dt[i] end
        dt = {}
    end--while
    -- don't forget the leading zeros ('1' is digit 0 in bitcoin base27 alphabet)
    return(concat(et))
--    return string.rep('1', zn) .. concat(et)
end --encode()

-- inverse base27 map, used by b27decode:  b27charmap maps characters in
-- base27 alphabet to their _offset_ in b27chars (0-based, not 1-based...)
--  eg.    for digit '1' b64charmap[65] == 0  and for 'z', b64charmap[122] == 57
--
local b27charmap = {};
for i = 1, 27 do b27charmap[byte(b27chars, i)] = i - 1  end

local function decode(s)
        if s==string.sub(b27chars,1,1) then return('') end
    -- reject invalid encoded strings
    if string.find(s, '[^'..b27chars..']') then
        return nil, 'invalid char'
    end
    -- process leading zeros - count and remove them
--    local zn -- number of leading zeros (base27 digits '1')
--    zn = #(string.match(s, '^(1+)') or '')
--    s = string.gsub(s, '^(1+)', '')
    -- special case: the string is empty or contains only null bytes
--    if s == '' then
--        return string.rep('\x00', zn)
--    end
    --
    -- process significant digits
    local dn -- decoded number as an array of bytes (little-endian)
    local d -- base27 digit, as an integer
    local b -- a byte in dn
    local m -- a byte multiplied by 27 (used for product)
    local carry
    dn = { b27charmap[byte(s, 1)] } --init with most significant digit
    for i = 2, #s do --repeat until no more digits
        -- multiply dn by 27, then add next digit
        d = b27charmap[byte(s, i)] -- next digit
        carry = 0
        -- multiply dn by 27
        for j = 1, #dn do
            b = dn[j]
            m = b * 27 + carry
            b = m & 0xff
            carry = m >> 8
            dn[j] = b
        end
        if carry > 0 then dn[#dn + 1] = carry end
        -- add next digit to dn
        carry = d
        for j = 1, #dn do
            b = dn[j] + carry
            carry = b >> 8
            dn[j] = b & 0xff
        end
        if carry > 0 then dn[#dn + 1] = carry end
    end
    -- now dn contains the decoded number (little endian)
    -- must add leading zeros and reverse dn to build binary string
    local ben = {} -- big-endian number as array of chars
    local ln = #dn
    for i = 1, ln do
        ben[i] = char(dn[ln-i+1])
    end
--    return string.rep('\x00', zn) .. concat(ben)
    return concat(ben)
end --decode()


return { -- base27 module
    encode = encode,
    decode = decode,
    }

end

base27 = preload_1008a()

--*****************************************************************************

function preload_1008u() 

local b27chars = 'ABCDE'..'FGHJK'..'LMNPQ'..'RSTWX'..'Y'..'345679'

local function encode(shex)   --  encode byte array to 27 chars string

    local hexbit        = bin.hextobin(shex)
    hexbit              = table.concat(hexbit)
    local encoded_block = ""

    while 0 == 0 do
    
        local bitsnr19 = 0

        for i=1,19 do
            bitsnr19 = bitsnr19 * 2
            if string.sub(hexbit,i,i) == "1" then
                bitsnr19 = bitsnr19 + 1
            end
            if i == #hexbit then
                break
            end
        end
        bitsnr19 = bitsnr19 + 0.001
        print(hexbit,bitsnr19)
        hexbit   = string.sub(hexbit,20)
        
        local encoded_block1 = ""
        while 0 == 0 do
--            print(bitsnr19)
            local pos     = math.floor(bitsnr19 % 27) + 1
            print("PP",pos)
            encoded_block1 = string.sub(b27chars,pos,pos) .. encoded_block1
            bitsnr19      = (bitsnr19 + 0.001) // 27 + 0.001
            print(bitsnr19,encoded_block1)
            if bitsnr19 < 0.5 then
                break
            end
        end
        encoded_block = encoded_block .. encoded_block1
        
        if hexbit == "" then
            break
        end
        
    end
--    print(encoded_block)
    
    return(encoded_block)
    
end

local function decode(s27)

    local decoded_bin = {}
    
    while 0 == 0 do

        local part_nr    = 0
        local len_block  = 19
        
        for i=1,4 do
            local char_aktuell = string.sub(s27,i,i)
--            if char_aktuell == "" then
--                len_block = 5*(i-1)-1
--            end
            part_nr = part_nr * 27
            part_nr = part_nr + string.find(b27chars,char_aktuell) - 1
            print(i,part_nr)
        end

        print("---------------",len_block)
        local part_hex = string.format("%0x",part_nr )
        print("QQ",s27,part_hex)
        local part_bin = bin.hextobin(part_hex)
        
        for i=1,len_block,1 do
            local a = part_bin[i]
            if a == nil then
                a = 0
            end
            table.insert(decoded_bin,a)
        end

--    print(">>>>",table.concat(decoded_bin),s27)
        s27            = string.sub(s27,5)

        if s27 == "" then
            break
        end
        
    end
    
--    while #decoded_bin % 4 > 0 do
--        table.insert(decoded_bin,"0")
--    end
    

    local result_hex_string = bin.bintohex( decoded_bin )
--    print(result_hex_string)
    
    return(result_hex_string)
    
 end   
            
return { -- base27 module
    encode = encode,
    decode = decode,
    }

end

base27precise = preload_1008u()

--*****************************************************************************

function preload_1008b()


local byte, char, concat = string.byte, string.char, table.concat

local b32chars = 'ABCDE'..'FGHJK'..'LMNPQ'..'RSTWX'..'YZ'..'0123456789'
local b32chars = 'RY1T9W2X'..'F0GZH4JK'..'SA67B3CDE'..'L5M8QPN'


local function encode(s)   --  encode byte array to 32 chars string
        if #s==0 then return string.sub(b32chars,1,1) end
    local q, b
    local et = {}
    local zn = 0     -- number of leading zero bytes in s
    -- assume s is a large, little-endian binary number
    -- with base256 digits (each byte is a 'digit')
    local nt = {} -- number to divide in base 256, big endian
    local dt = {} -- result of nt // 32, in base 256
    local more = true  -- used to count leading zero bytes
    for i = 1, #s do
        b = byte(s, i)
--        if more and b == 0 then
--            zn = zn + 1
--        else
--            more = false
--        end
        nt[i] = b
    end
--    if #s == zn then --take care of strings empty or with only nul bytes
--        return string.rep('1', zn)
--    end
    more = true
    while more do
        local r = 0
        more = false
        for i = 1, #nt do
            b = nt[i] + (256 * r)
            q = b // 32
            -- if q is not null at least once, we are good
            -- for another division by 32
            more = more or q > 0
            r = b % 32
            dt[i] = q
        end
        -- r is the next base32 digit. insert it before previous ones
        -- to get a big-endian base32 number
        table.insert(et, 1, char(byte(b32chars, r+1)))
        -- now copy dt into nt before another round of division by 32
        nt = {}
        for i = 1, #dt do nt[i] = dt[i] end
        dt = {}
    end--while
    -- don't forget the leading zeros ('1' is digit 0 in bitcoin base32 alphabet)
    return concat(et)
--    return string.rep('1', zn) .. concat(et)
end --encode()

-- inverse base32 map, used by b32decode:  b32charmap maps characters in
-- base32 alphabet to their _offset_ in b32chars (0-based, not 1-based...)
--  eg.    for digit '1' b64charmap[65] == 0  and for 'z', b64charmap[122] == 57
--
local b32charmap = {};
for i = 1, 32 do b32charmap[byte(b32chars, i)] = i - 1  end

local function decode(s)
    -- reject invalid encoded strings
        if s==string.sub(b32chars,1,1) then return('') end
    if string.find(s, '[^'..b32chars..']') then
        return nil, 'invalid char'
    end
    -- process leading zeros - count and remove them
    local zn -- number of leading zeros (base32 digits '1')
--    zn = #(string.match(s, '^(1+)') or '')
--    s = string.gsub(s, '^(1+)', '')
    -- special case: the string is empty or contains only null bytes
--    if s == '' then
--        return string.rep('\x00', zn)
--    end
    --
    -- process significant digits
    local dn -- decoded number as an array of bytes (little-endian)
    local d -- base32 digit, as an integer
    local b -- a byte in dn
    local m -- a byte multiplied by 32 (used for product)
    local carry
    dn = { b32charmap[byte(s, 1)] } --init with most significant digit
    for i = 2, #s do --repeat until no more digits
        -- multiply dn by 32, then add next digit
        d = b32charmap[byte(s, i)] -- next digit
        carry = 0
        -- multiply dn by 32
        for j = 1, #dn do
            b = dn[j]
            m = b * 32 + carry
            b = m & 0xff
            carry = m >> 8
            dn[j] = b
        end
        if carry > 0 then dn[#dn + 1] = carry end
        -- add next digit to dn
        carry = d
        for j = 1, #dn do
            b = dn[j] + carry
            carry = b >> 8
            dn[j] = b & 0xff
        end
        if carry > 0 then dn[#dn + 1] = carry end
    end
    -- now dn contains the decoded number (little endian)
    -- must add leading zeros and reverse dn to build binary string
    local ben = {} -- big-endian number as array of chars
    local ln = #dn
    for i = 1, ln do
        ben[i] = char(dn[ln-i+1])
    end
    return concat(ben)
--    return string.rep('\x00', zn) .. concat(ben)
end --decode()


return { -- base32 module
    encode = encode,
    decode = decode,
    }

end


base32 = preload_1008b()


--*****************************************************************************

function preload_1008c()


local byte, char, concat = string.byte, string.char, table.concat

local b64chars = 'ABCDE'..'FGHJK'..'LMNPQ'..'RSTWX'..'YZ'..'0123456789'
local b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'..'abcdefghijklmnopqrstuvwxyz_-'


local function encode(s)   --  encode byte array to 64 chars string
        if #s==0 then return string.sub(b64chars,1,1) end
    local q, b
    local et = {}
    local zn = 0     -- number of leading zero bytes in s
    -- assume s is a large, little-endian binary number
    -- with base256 digits (each byte is a 'digit')
    local nt = {} -- number to divide in base 256, big endian
    local dt = {} -- result of nt // 64, in base 256
    local more = true  -- used to count leading zero bytes
    for i = 1, #s do
        b = byte(s, i)
--        if more and b == 0 then
--            zn = zn + 1
--        else
--            more = false
--        end
        nt[i] = b
    end
--    if #s == zn then --take care of strings empty or with only nul bytes
--        return string.rep('1', zn)
--    end
    more = true
    while more do
        local r = 0
        more = false
        for i = 1, #nt do
            b = nt[i] + (256 * r)
            q = b // 64
            -- if q is not null at least once, we are good
            -- for another division by 64
            more = more or q > 0
            r = b % 64
            dt[i] = q
        end
        -- r is the next base64 digit. insert it before previous ones
        -- to get a big-endian base64 number
        table.insert(et, 1, char(byte(b64chars, r+1)))
        -- now copy dt into nt before another round of division by 64
        nt = {}
        for i = 1, #dt do nt[i] = dt[i] end
        dt = {}
    end--while
    -- don't forget the leading zeros ('1' is digit 0 in bitcoin base64 alphabet)
    return concat(et)
--    return string.rep('1', zn) .. concat(et)
end --encode()

-- inverse base64 map, used by b64decode:  b64charmap maps characters in
-- base64 alphabet to their _offset_ in b64chars (0-based, not 1-based...)
--  eg.    for digit '1' b64charmap[65] == 0  and for 'z', b64charmap[122] == 57
--
local b64charmap = {};
for i = 1, 64 do b64charmap[byte(b64chars, i)] = i - 1  end

local function decode(s)
    -- reject invalid encoded strings
        if s==string.sub(b64chars,1,1) then return('') end
    if string.find(s, '[^'..b64chars..']') then
        return nil, 'invalid char'
    end
    -- process leading zeros - count and remove them
    local zn -- number of leading zeros (base64 digits '1')
--    zn = #(string.match(s, '^(1+)') or '')
--    s = string.gsub(s, '^(1+)', '')
    -- special case: the string is empty or contains only null bytes
--    if s == '' then
--        return string.rep('\x00', zn)
--    end
    --
    -- process significant digits
    local dn -- decoded number as an array of bytes (little-endian)
    local d -- base64 digit, as an integer
    local b -- a byte in dn
    local m -- a byte multiplied by 64 (used for product)
    local carry
    dn = { b64charmap[byte(s, 1)] } --init with most significant digit
    for i = 2, #s do --repeat until no more digits
        -- multiply dn by 64, then add next digit
        d = b64charmap[byte(s, i)] -- next digit
        carry = 0
        -- multiply dn by 64
        for j = 1, #dn do
            b = dn[j]
            m = b * 64 + carry
            b = m & 0xff
            carry = m >> 8
            dn[j] = b
        end
        if carry > 0 then dn[#dn + 1] = carry end
        -- add next digit to dn
        carry = d
        for j = 1, #dn do
            b = dn[j] + carry
            carry = b >> 8
            dn[j] = b & 0xff
        end
        if carry > 0 then dn[#dn + 1] = carry end
    end
    -- now dn contains the decoded number (little endian)
    -- must add leading zeros and reverse dn to build binary string
    local ben = {} -- big-endian number as array of chars
    local ln = #dn
    for i = 1, ln do
        ben[i] = char(dn[ln-i+1])
    end
    return concat(ben)
--    return string.rep('\x00', zn) .. concat(ben)
end --decode()


return { -- base64 module
    encode = encode,
    decode = decode,
    }

end


base64 = preload_1008c()


--*****************************************************************************

function preload_1008d()


local byte, char, concat = string.byte, string.char, table.concat

local b62chars = 'ABCDE'..'FGHJK'..'LMNPQ'..'RSTWX'..'YZ'..'0123456789'
local b62chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'..'abcdefghijklmnopqrstuvwxyz'


local function encode(s)   --  encode byte array to 62 chars string
        if #s==0 then return string.sub(b62chars,1,1) end
    local q, b
    local et = {}
    local zn = 0     -- number of leading zero bytes in s
    -- assume s is a large, little-endian binary number
    -- with base256 digits (each byte is a 'digit')
    local nt = {} -- number to divide in base 256, big endian
    local dt = {} -- result of nt // 62, in base 256
    local more = true  -- used to count leading zero bytes
    for i = 1, #s do
        b = byte(s, i)
--        if more and b == 0 then
--            zn = zn + 1
--        else
--            more = false
--        end
        nt[i] = b
    end
--    if #s == zn then --take care of strings empty or with only nul bytes
--        return string.rep('1', zn)
--    end
    more = true
    while more do
        local r = 0
        more = false
        for i = 1, #nt do
            b = nt[i] + (256 * r)
            q = b // 62
            -- if q is not null at least once, we are good
            -- for another division by 62
            more = more or q > 0
            r = b % 62
            dt[i] = q
        end
        -- r is the next base62 digit. insert it before previous ones
        -- to get a big-endian base62 number
        table.insert(et, 1, char(byte(b62chars, r+1)))
        -- now copy dt into nt before another round of division by 62
        nt = {}
        for i = 1, #dt do nt[i] = dt[i] end
        dt = {}
    end--while
    -- don't forget the leading zeros ('1' is digit 0 in bitcoin base62 alphabet)
    return concat(et)
--    return string.rep('1', zn) .. concat(et)
end --encode()

-- inverse base62 map, used by b62decode:  b62charmap maps characters in
-- base62 alphabet to their _offset_ in b62chars (0-based, not 1-based...)
--  eg.    for digit '1' b62charmap[65] == 0  and for 'z', b62charmap[122] == 57
--
local b62charmap = {};
for i = 1, 62 do b62charmap[byte(b62chars, i)] = i - 1  end

local function decode(s)
    -- reject invalid encoded strings
        if s==string.sub(b62chars,1,1) then return('') end
    if string.find(s, '[^'..b62chars..']') then
        return nil, 'invalid char'
    end
    -- process leading zeros - count and remove them
    local zn -- number of leading zeros (base62 digits '1')
--    zn = #(string.match(s, '^(1+)') or '')
--    s = string.gsub(s, '^(1+)', '')
    -- special case: the string is empty or contains only null bytes
--    if s == '' then
--        return string.rep('\x00', zn)
--    end
    --
    -- process significant digits
    local dn -- decoded number as an array of bytes (little-endian)
    local d -- base62 digit, as an integer
    local b -- a byte in dn
    local m -- a byte multiplied by 62 (used for product)
    local carry
    dn = { b62charmap[byte(s, 1)] } --init with most significant digit
    for i = 2, #s do --repeat until no more digits
        -- multiply dn by 62, then add next digit
        d = b62charmap[byte(s, i)] -- next digit
        carry = 0
        -- multiply dn by 62
        for j = 1, #dn do
            b = dn[j]
            m = b * 62 + carry
            b = m & 0xff
            carry = m >> 8
            dn[j] = b
        end
        if carry > 0 then dn[#dn + 1] = carry end
        -- add next digit to dn
        carry = d
        for j = 1, #dn do
            b = dn[j] + carry
            carry = b >> 8
            dn[j] = b & 0xff
        end
        if carry > 0 then dn[#dn + 1] = carry end
    end
    -- now dn contains the decoded number (little endian)
    -- must add leading zeros and reverse dn to build binary string
    local ben = {} -- big-endian number as array of chars
    local ln = #dn
    for i = 1, ln do
        ben[i] = char(dn[ln-i+1])
    end
    return concat(ben)
--    return string.rep('\x00', zn) .. concat(ben)
end --decode()


local function encode(s) return(bin.stohex(s)) end
local function decode(s) return(bin.hextos(s)) end


return { -- base62 module
    encode = encode,
    decode = decode,
    }

end


base62 = preload_1008d()


--*****************************************************************************

function preload_1001()


function preload_2002()


local app, concat = table.insert, table.concat

------------------------------------------------------------

-- salsa quarter round (rotl inlined)
local function qround(st,x,y,z,w)
    -- st is a salsa state: an array of 16 u32 words
    -- x,y,z,w are indices in st
    local a, b, c, d = st[x], st[y], st[z], st[w]
    local t
    t = (a + d) & 0xffffffff
    -- b = b ~ rotl32(t, 7)
    b = b ~ ((t << 7) | (t >> 25)) & 0xffffffff
    t = (b + a) & 0xffffffff
    -- c = c ~ rotl32(t, 9)
    c = c ~ ((t << 9) | (t >> 23)) & 0xffffffff
    t = (c + b) & 0xffffffff
    -- d = d ~ rotl32(t, 13)
    d = d ~ ((t << 13) | (t >> 19)) & 0xffffffff
    t = (d + c) & 0xffffffff
    -- a = a ~ rotl32(t, 18)
    a = a ~ ((t << 18) | (t >> 14)) & 0xffffffff
    st[x], st[y], st[z], st[w] = a, b, c, d
    return st
end



-- salsa20 state and working state are allocated once and reused
-- by each invocation of salsa20_block()
local salsa20_state = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
local salsa20_working_state = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}

local salsa20_block = function(key, counter, nonce)
    -- key: u32[8]
    -- counter: u32[2]
    -- nonce: u32[2]
    local st = salsa20_state         -- state
    local wst = salsa20_working_state     -- working state
    -- initialize state
    st[1], st[6], st[11], st[16] =
        0x61707865, 0x3320646e, 0x79622d32, 0x6b206574
    for i = 1, 4 do
        st[i+1] = key[i]
        st[i+11] = key[i+4]
    end
    st[7], st[8], st[9], st[10] = nonce[1], nonce[2], counter[1], counter[2]
    -- copy state to working_state
    for i = 1, 16 do wst[i] = st[i] end
    -- run 20 rounds, ie. 10 iterations of 8 quarter rounds
    for _ = 1, 10 do           --RFC reference:
        qround(wst, 1,5,9,13)    --1.  QUARTERROUND ( 0, 4, 8,12)
        qround(wst, 6,10,14,2)   --2.  QUARTERROUND ( 5, 9,13, 1)
        qround(wst, 11,15,3,7)   --3.  QUARTERROUND (10,14, 2, 6)
        qround(wst, 16,4,8,12)   --4.  QUARTERROUND (15, 3, 7,11)
        qround(wst, 1,2,3,4)     --5.  QUARTERROUND ( 0, 1, 2, 3)
        qround(wst, 6,7,8,5)     --6.  QUARTERROUND ( 5, 6, 7, 4)
        qround(wst, 11,12,9,10)  --7.  QUARTERROUND (10,11, 8, 9)
        qround(wst, 16,13,14,15) --8.  QUARTERROUND (15,12,13,14)
    end
    -- add working_state to state
    for i = 1, 16 do st[i] = (st[i] + wst[i]) & 0xffffffff end
    -- return st, an array of 16 u32 words used as a keystream
    return st
end --salsa20_block()

local function hsalsa20_block(key, counter, nonce)
    error2("salsa0001")
    local st = salsa20_block(key, counter, nonce)
    error2("salsa0002")
    return {
        (st[1] - 0x61707865) & 0xffffffff,
        (st[6] - 0x3320646e) & 0xffffffff,
        (st[11] - 0x79622d32) & 0xffffffff,
        (st[16] - 0x6b206574) & 0xffffffff,
        (st[7] - nonce[1]) & 0xffffffff,
        (st[8] - nonce[2]) & 0xffffffff,
        (st[9] - counter[1]) & 0xffffffff,
        (st[10] - counter[2]) & 0xffffffff,
    }
end

-- pat16: used to unpack a 64-byte string as 16 uint32 and vice versa
local pat16 = '<I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4'

-- pat8: used to unpack a 32-byte string as 8 uint32 and vice versa
local pat8 = '<I4I4I4I4I4I4I4I4'

local function salsa20_encrypt_block(key, counter, nonce, pt, ptidx)
    -- encrypt a 64-byte block of plain text.
    -- key: 32 bytes as an array of 8 uint32
    -- counter: 8 bytes as an array of 2 uint32
    -- nonce: 8 bytes as an array of 2 uint32
    -- pt: plain text string,
    -- ptidx: index of beginning of block in plain text (origin=1)
    -- if less than 64 bytes are left at position ptidx, it is padded
    --    with null bytes before encryption and result is stripped
    --    accordingly.
    -- return encrypted block as a string  (length <= 16)
    local rbn = #pt - ptidx + 1 -- number of remaining bytes in pt
    if rbn < 64 then
        local tmp = string.sub(pt, ptidx)
        pt = tmp .. string.rep('\0', 64 - rbn) --pad last block
        ptidx = 1
    end
    assert(#pt >= 64)
    local ba = table.pack(string.unpack(pat16, pt, ptidx))
    local keystream = salsa20_block(key, counter, nonce)
    for i = 1, 16 do
        ba[i] = ba[i] ~ keystream[i]
    end
    local es = string.pack(pat16, table.unpack(ba))
    if rbn < 64 then
        es = string.sub(es, 1, rbn)
    end
    return es
end --salsa20_encrypt_block

local salsa20_encrypt = function(key, counter, nonce, pt)
    -- encrypt plain text 'pt', return encrypted text
    -- key: 32 bytes as a string
    -- counter: an uint64 (must be incremented for each block)
    -- nonce: 8 bytes as a string
    -- pt: plain text string
    assert(#key == 32, '#key must be 32')
    assert(#nonce == 8, '#nonce must be 8')
    local keya = table.pack(string.unpack('<I4I4I4I4I4I4I4I4', key))
    local noncea = table.pack(string.unpack('<I4I4', nonce))
    local countera = {counter & 0xffffffff, counter >> 32}
    local t = {} -- used to collect all encrypted blocks
    local ptidx = 1
    while ptidx <= #pt do
--        feed_the_dog()
        app(t, salsa20_encrypt_block(keya, countera, noncea, pt, ptidx))
        ptidx = ptidx + 64
        countera[1] = countera[1] + 1
        if countera[1] > 0xffffffff then
            countera[1] = 0
            countera[2] = countera[2] + 1
        end
    end
    return (concat(t))
end --salsa20_encrypt()

local function salsa20_stream(key, counter, nonce, length)
    assert(#key == 32, '#key must be 32')
    assert(#nonce == 8, '#nonce must be 8')
    local keya = table.pack(string.unpack('<I4I4I4I4I4I4I4I4', key))
    local noncea = table.pack(string.unpack('<I4I4', nonce))
    local countera = {counter & 0xffffffff, counter >> 32}
    local t = {} -- used to collect all encrypted blocks
    while length > 0 do
--        feed_the_dog()
        local keystream = salsa20_block(keya, countera, noncea)
        local block = string.pack(pat16, table.unpack(keystream))
        if length <= 64 then block = block:sub(1, length) end
        app(t, block)
        length = length - 64
        countera[1] = countera[1] + 1
        if countera[1] > 0xffffffff then
            countera[1] = 0
            countera[2] = countera[2] + 1
        end
    end
    return (concat(t))
end

local hsalsa20 = function(key, counter, nonce)
    assert(#key == 32, '#key must be 32')
    assert(#nonce == 8, '#nonce must be 8')
    local keya = table.pack(string.unpack('<I4I4I4I4I4I4I4I4', key))
    local noncea = table.pack(string.unpack('<I4I4', nonce))
    local countera = {counter & 0xffffffff, counter >> 32}
    local stream = hsalsa20_block(keya, countera, noncea)
    return string.pack(pat8, table.unpack(stream))
end

------------------------------------------------------------
return {
    encrypt = salsa20_encrypt,
    decrypt = salsa20_encrypt,
    stream = salsa20_stream,
    hsalsa20 = hsalsa20,
    --
    key_size = 32,
    nonce_size = 8,
    }

--end of salsa20

end


salsa20 = preload_2002()

        
function preload_2003()


------------------------------------------------------------

-- set25519() not used

local function car25519(o)
    local c
    for i = 1, 16 do
        o[i] = o[i] + 65536 -- 1 << 16
        -- lua '>>' doesn't perform sign extension...
        -- so the following >>16 doesn't work with negative numbers!!
        -- ...took a bit of time to find this one :-)
        -- c = o[i] >> 16
        c = o[i] // 65536
        if i < 16 then
            o[i+1] = o[i+1] + (c - 1)
        else
            o[1] = o[1] + 38 * (c - 1)
        end
        o[i] = o[i] - (c << 16)
    end
end --car25519()

local function sel25519(p, q, b)
    local c = ~(b-1)
    local t
    for i = 1, 16 do
        t = c & (p[i] ~ q[i])
        p[i] = p[i] ~ t
        q[i] = q[i] ~ t
    end
end --sel25519

local function pack25519(o, n)
    -- out o[32], in n[16]
    local m, t = {}, {}
    local b
    for i = 1, 16 do t[i] = n[i] end
    car25519(t)
    car25519(t)
    car25519(t)
    for _ = 1, 2 do
        m[1] = t[1] - 0xffed
        for i = 2, 15 do
            m[i] = t[i] - 0xffff - ((m[i-1] >> 16) & 1)
            m[i-1] = m[i-1] & 0xffff
        end
        m[16] = t[16] - 0x7fff - ((m[15] >> 16) & 1)
        b = (m[16] >> 16) & 1
        m[15] = m[15] & 0xffff
        sel25519(t, m, 1-b)
    end
    for i = 1, 16 do
        o[2*i-1] = t[i] & 0xff
        o[2*i] = t[i] >> 8
    end
end -- pack25519

-- neq25519() not used
-- par25519() not used

local function unpack25519(o, n)
    -- out o[16], in n[32]
    for i = 1, 16 do
        o[i] = n[2*i-1] + (n[2*i] << 8)
    end
    o[16] = o[16] & 0x7fff
end -- unpack25519

local function A(o, a, b) --add
    for i = 1, 16 do o[i] = a[i] + b[i] end
end

local function Z(o, a, b) --sub
    for i = 1, 16 do o[i] = a[i] - b[i] end
end

local function M(o, a, b) --mul  gf, gf -> gf
    local t = {}
    for i = 1, 32 do t[i] = 0  end
    for i = 1, 16 do
        for j = 1, 16 do
            t[i+j-1] = t[i+j-1] + (a[i] * b[j])
        end
    end
    for i = 1, 15 do t[i] = t[i] + 38 * t[i+16] end
    for i = 1, 16 do o[i] = t[i] end
    car25519(o)
    car25519(o)
end

local function S(o, a)  --square
    M(o, a, a)
end

local function inv25519(o, i)
    local c = {}
    for a = 1, 16 do c[a] = i[a] end
    for a = 253, 0, -1 do
        S(c, c)
        if a ~= 2 and a ~= 4 then M(c, c, i) end
    end
    for a = 1, 16 do o[a] = c[a] end
--~     pt(o)
end

--pow2523() not used

local t_121665 = {0xDB41,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}

local function crypto_scalarmult(q, n, p)
    -- out q[], in n[], in p[]
    local z = {}
    local x = {}
    local a = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    local b = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    local c = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    local d = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    local e = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    local f = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    for i = 1, 31 do z[i] = n[i] end
    z[32] = (n[32] & 127) | 64
    z[1] = z[1] & 248
--~     pt(z)
    unpack25519(x, p)
--~     pt(x)
    for i = 1, 16 do
        b[i] = x[i]
        a[i] = 0
        c[i] = 0
        d[i] = 0
    end
    a[1] = 1
    d[1] = 1
    for i = 254, 0, -1 do              --  urspruenglich 254
        if i%10 == 0 and file_open ~= nil then
            if file_open ~= nil then
                start_timer(26,WAIT_26,0,1)
--                error2("crypto_scalarmult_0001 "..tostring(i))
                coroutine.yield()
            else
                notify("Crypto, WAIT26")
            end
        end
        local r = (z[(i>>3)+1] >> (i & 7)) & 1
        sel25519(a,b,r)
        sel25519(c,d,r)
        A(e,a,c)
        Z(a,a,c)
        A(c,b,d)
        Z(b,b,d)
        S(d,e)
        S(f,a)
        M(a,c,a)
        M(c,b,e)
        A(e,a,c)
        Z(a,a,c)
--        error2("crypto_scalarmult_0155 "..tostring(i))
        S(b,a)
        Z(c,d,f)
        M(a,c,t_121665)
        A(a,a,d)
        M(c,c,a)
        M(a,d,f)
        M(d,b,x)
        S(b,e)
--        error2("crypto_scalarmult_0156 "..tostring(i))
        sel25519(a,b,r)
        sel25519(c,d,r)
    end
                error2("crypto_scalarmult_0002")
    for i = 1, 16 do
        x[i+16] = a[i]
        x[i+32] = c[i]
        x[i+48] = b[i]
        x[i+64] = d[i]
    end
    -- cannot use pointer arithmetics...
    local x16, x32 = {}, {}
    for i = 1, #x do
        if i > 16 then x16[i-16] = x[i] end
        if i > 32 then x32[i-32] = x[i] end
    end
                error2("crypto_scalarmult_0003")
    inv25519(x32,x32)
                error2("crypto_scalarmult_0004")
    M(x16,x16,x32)
                error2("crypto_scalarmult_0005")
    pack25519(q,x16)
                error2("crypto_scalarmult_0006")
            if file_open ~= nil then
                start_timer(26,WAIT_26,0,1)
                error2("crypto_scalarmult_0009 ")
                coroutine.yield()
            else
                notify("Crypto, WAIT26")
            end
    return 0
end -- crypto_scalarmult

local t_9 = { -- u8 * 32
    9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    }

local function crypto_scalarmult_base(q, n)
    -- out q[], in n[]
    return crypto_scalarmult(q, n, t_9)
end

------------------------------------------------------------------------
-- convenience function (using binary strings instead of byte tables)
--
-- curve point and scalars are represented as 32-byte binary strings
-- (encoded as little endian)

local function scalarmult(n, p)
    -- n, a scalar (little endian) as a 32-byte string
    -- p, a curve point as a 32-byte string
    -- return the scalar product np as a 32-byte string
    local qt, nt, pt = {}, {}, {} 
    for i = 1, 32 do 
        nt[i] = string.byte(n, i) 
        pt[i] = string.byte(p, i) 
    end
    crypto_scalarmult(qt, nt, pt)
    local q = string.char(table.unpack(qt))
    return q
end

-- base: the curve point generator = 9

local base = '\9' .. ('\0'):rep(31)


return {
    crypto_scalarmult   = crypto_scalarmult,
    crypto_scalarmult_base = crypto_scalarmult_base,
    --
    -- convenience function and definition
    --
    scalarmult = scalarmult,
    base = base,
    --
}
 -- end of ec25519 module


end

ec25519 = preload_2003()


        
function preload_2004()


-----------------------------------------------------------
-- poly1305

local sunp = string.unpack

local function poly_init(k)
    -- k: 32-byte key as a string
    -- initialize internal state
    local st = {
        r = {
            (sunp('<I4', k,  1)     ) & 0x3ffffff,  --r0
            (sunp('<I4', k,  4) >> 2) & 0x3ffff03,  --r1
            (sunp('<I4', k,  7) >> 4) & 0x3ffc0ff,  --r2
            (sunp('<I4', k, 10) >> 6) & 0x3f03fff,  --r3
            (sunp('<I4', k, 13) >> 8) & 0x00fffff,  --r4
        },
        h = { 0,0,0,0,0 },
        pad = {    sunp('<I4', k, 17),  -- 's' in rfc
            sunp('<I4', k, 21),
            sunp('<I4', k, 25),
            sunp('<I4', k, 29),
        },
        buffer = '', --
        leftover = 0,
        final = false,
    }--st
    return st
end --poly_init()

local function poly_blocks(st, m)
    -- st: internal state
    -- m: message:string
    local bytes = #m
    local midx = 1
    local hibit = st.final and 0 or 0x01000000 -- 1 << 24
    local r0 = st.r[1]
    local r1 = st.r[2]
    local r2 = st.r[3]
    local r3 = st.r[4]
    local r4 = st.r[5]
    local s1 = r1 * 5
    local s2 = r2 * 5
    local s3 = r3 * 5
    local s4 = r4 * 5
    local h0 = st.h[1]
    local h1 = st.h[2]
    local h2 = st.h[3]
    local h3 = st.h[4]
    local h4 = st.h[5]
    local d0, d1, d2, d3, d4, c
    --
    while bytes >= 16 do  -- 16 = poly1305_block_size
        -- h += m[i]  (in rfc:  a += n with 0x01 byte)
        h0 = h0 + ((sunp('<I4', m, midx     )     ) & 0x3ffffff)
        h1 = h1 + ((sunp('<I4', m, midx +  3) >> 2) & 0x3ffffff)
        h2 = h2 + ((sunp('<I4', m, midx +  6) >> 4) & 0x3ffffff)
        h3 = h3 + ((sunp('<I4', m, midx +  9) >> 6) & 0x3ffffff)
        h4 = h4 + ((sunp('<I4', m, midx + 12) >> 8) | hibit)--0x01 byte
        --
        -- h *= r % p (partial)
        d0 = h0*r0 + h1*s4 + h2*s3 + h3*s2 + h4*s1
        d1 = h0*r1 + h1*r0 + h2*s4 + h3*s3 + h4*s2
        d2 = h0*r2 + h1*r1 + h2*r0 + h3*s4 + h4*s3
        d3 = h0*r3 + h1*r2 + h2*r1 + h3*r0 + h4*s4
        d4 = h0*r4 + h1*r3 + h2*r2 + h3*r1 + h4*r0
        --
                      c = (d0>>26) & 0xffffffff ; h0 = d0 & 0x3ffffff
        d1 = d1 + c ; c = (d1>>26) & 0xffffffff ; h1 = d1 & 0x3ffffff
        d2 = d2 + c ; c = (d2>>26) & 0xffffffff ; h2 = d2 & 0x3ffffff
        d3 = d3 + c ; c = (d3>>26) & 0xffffffff ; h3 = d3 & 0x3ffffff
        d4 = d4 + c ; c = (d4>>26) & 0xffffffff ; h4 = d4 & 0x3ffffff
        h0 = h0 + (c*5) ; c = h0>>26 ; h0 = h0 & 0x3ffffff
        h1 = h1 + c
        --
        midx = midx + 16 -- 16 = poly1305_block_size
        bytes = bytes - 16
    end --while
    st.h[1] = h0
    st.h[2] = h1
    st.h[3] = h2
    st.h[4] = h3
    st.h[5] = h4
    st.bytes = bytes -- remaining bytes. must be < 16 here
    st.midx = midx -- index of first remaining bytes
    return st
end --poly_blocks()

local function poly_update(st, m)
    -- st: internal state
    -- m: message:string
    st.bytes, st.midx = #m, 1
    -- process full blocks if any
    if st.bytes >= 16 then
        poly_blocks(st, m)
    end
    --handle remaining bytes
    if st.bytes == 0 then -- no bytes left
        -- nothing to do? no add 0x01? - apparently not.
    else
        local buffer =     string.sub(m, st.midx)
            .. '\x01' .. string.rep('\0', 16 - st.bytes -1)
        assert(#buffer == 16)
        st.final = true  -- this is the last block
--~         p16(buffer)
        poly_blocks(st, buffer)
    end
    --
    return st
end --poly_update

local function poly_finish(st)
    --
    local c, mask     --u32
    local f      --u64
    -- fully carry h
    local h0 = st.h[1]
    local h1 = st.h[2]
    local h2 = st.h[3]
    local h3 = st.h[4]
    local h4 = st.h[5]
    --
                 c = h1 >> 26; h1 = h1 & 0x3ffffff
    h2 = h2 +     c; c = h2 >> 26; h2 = h2 & 0x3ffffff
    h3 = h3 +     c; c = h3 >> 26; h3 = h3 & 0x3ffffff
    h4 = h4 +     c; c = h4 >> 26; h4 = h4 & 0x3ffffff
    h0 = h0 + (c*5); c = h0 >> 26; h0 = h0 & 0x3ffffff
    h1 = h1 + c
    --
    --compute h + -p
    local g0 = (h0 + 5) ; c = g0 >> 26; g0 = g0 & 0x3ffffff
    local g1 = (h1 + c) ; c = g1 >> 26; g1 = g1 & 0x3ffffff
    local g2 = (h2 + c) ; c = g2 >> 26; g2 = g2 & 0x3ffffff
    local g3 = (h3 + c) ; c = g3 >> 26; g3 = g3 & 0x3ffffff
    local g4 = (h4 + c - 0x4000000) &0xffffffff  -- (1 << 26)
    --
    -- select h if h < p, or h + -p if h >= p
    mask = ((g4 >> 31) -1) & 0xffffffff
    --
    g0 = g0 & mask
    g1 = g1 & mask
    g2 = g2 & mask
    g3 = g3 & mask
    g4 = g4 & mask
    --
    mask = (~mask)  & 0xffffffff
    h0 = (h0 & mask) | g0
    h1 = (h1 & mask) | g1
    h2 = (h2 & mask) | g2
    h3 = (h3 & mask) | g3
    h4 = (h4 & mask) | g4
    --
    --h = h % (2^128)
    h0 = ((h0      ) | (h1 << 26)) & 0xffffffff
    h1 = ((h1 >>  6) | (h2 << 20)) & 0xffffffff
    h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff
    h3 = ((h3 >> 18) | (h4 <<  8)) & 0xffffffff
    --
    -- mac = (h + pad) % (2^128)
    f = h0 + st.pad[1]             ; h0 = f & 0xffffffff
    f = h1 + st.pad[2] + (f >> 32) ; h1 = f & 0xffffffff
    f = h2 + st.pad[3] + (f >> 32) ; h2 = f & 0xffffffff
    f = h3 + st.pad[4] + (f >> 32) ; h3 = f & 0xffffffff
    --
    local mac = string.pack('<I4I4I4I4', h0, h1, h2, h3)
    -- (should zero out the state?)
    --
    return mac
end --poly_finish()

local function poly_auth(m, k)
    -- m: msg string
    -- k: key string (must be 32 bytes)
    -- return mac 16-byte string
    assert(#k == 32)
    local st = poly_init(k)
    poly_update(st, m)
    local mac = poly_finish(st)
    return mac
end --poly_auth()

local function poly_verify(m, k, mac)
    local macm = poly_auth(m, k)
    return macm == mac
end --poly_verify()

------------------------------------------------------------
-- return poly1305 module

return {
    init = poly_init,
    update = poly_update,
    finish = poly_finish,
    auth = poly_auth,
    verify = poly_verify,
    }

end

local poly1305 = preload_2004()


local function public_key(sk)
    assert(type(sk) == 'string', 'sk must be a string')
    assert(#sk == 32, '#sh must be 32')
    sk = table.pack(sk:byte(1, 32))
    local pk = {}
    ec25519.crypto_scalarmult_base(pk, sk)
    return string.char(table.unpack(pk))
end

local function unpack_nonce(nonce)
    assert(#nonce == 24, '#nonce must be 24')
    local nonce1 = nonce:sub(1, 8)
    local counter = string.unpack('<I8', nonce:sub(9,16))
    local nonce2 = nonce:sub(17,24)
    return counter, nonce1, nonce2
end

local function secretbox(pt, nonce, key)
    assert(#key == 32, '#key must be 32')
    local counter, nonce1, nonce2 = unpack_nonce(nonce)
    local key2 = salsa20.hsalsa20(key, counter, nonce1)
    local et = salsa20.encrypt(key2, 0, nonce2, string.rep('\0', 32) .. pt)
    local key3 = et:sub(1, 32)
    et = et:sub(33)
    local mac = poly1305.auth(et, key3)
    return mac .. et
end

local function secretbox_open(et, nonce, key)
    assert(#key == 32, '#key must be 32')
    assert(#et >= 16, '#et must be at least 16')
    local counter, nonce1, nonce2 = unpack_nonce(nonce)
    local key2 = salsa20.hsalsa20(key, counter, nonce1)
    local key3 = salsa20.stream(key2, 0, nonce2, 32)
    local mac = et:sub(1, 16)
    local mac2 = poly1305.auth(et:sub(17), key3)
    if mac2 ~= mac then return nil, 'invalid MAC' end
    local pt = salsa20.encrypt(key2, 0, nonce2, string.rep('\0', 16) .. et)
    return pt:sub(33)
end

local function stream_key(pk, sk)
    assert(#pk == 32, '#pk must be 32')
    assert(#sk == 32, '#pk must be 32')
    pk = table.pack(pk:byte(1, 32))
    sk = table.pack(sk:byte(1, 32))
    local k = {}
    ec25519.crypto_scalarmult(k, sk, pk)
    k = string.char(table.unpack(k))
    return salsa20.hsalsa20(k, 0, string.rep('\0', 8))
end

local function box(pt, nonce, pk_b, sk_a)
    return secretbox(pt, nonce, stream_key(pk_b, sk_a))
end

local function box_open(et, nonce, pk_a, sk_b)
    return secretbox_open(et, nonce, stream_key(pk_a, sk_b))
end

return {
    public_key = public_key,
    secretbox = secretbox,
    secretbox_open = secretbox_open,
    stream_key = stream_key,
    box = box,
    box_open = box_open,
}

end


crypto = preload_1001()

--********************************************************************************
        
function preload_1009()

-- some local definitions

local strf = string.format
local byte, char = string.byte, string.char
local spack, sunpack = string.pack, string.unpack

local app, concat = table.insert, table.concat

local transform_table = {

    ['0']    = '0000',
    ['1']    = '0001',
    ['2']    = '0010',
    ['3']    = '0011',
    ['4']    = '0100',
    ['5']    = '0101',
    ['6']    = '0110',
    ['7']    = '0111',
    ['8']    = '1000',
    ['9']    = '1001',
    ['a']    = '1010',
    ['b']    = '1011',
    ['c']    = '1100',
    ['d']    = '1101',
    ['e']    = '1110',
    ['f']    = '1111',
    
    ['0000'] = '0',
    ['0001'] = '1',
    ['0010'] = '2',
    ['0011'] = '3',
    ['0100'] = '4',
    ['0101'] = '5',
    ['0110'] = '6',
    ['0111'] = '7',
    ['1000'] = '8',
    ['1001'] = '9',
    ['1010'] = 'a',
    ['1011'] = 'b',
    ['1100'] = 'c',
    ['1101'] = 'd',
    ['1110'] = 'e',
    ['1111'] = 'f'   }
    

------------------------------------------------------------------------

local function bintohex (h)

    local text = {}
    h          = table.concat(h)
    while h ~= '' do
        h1 = string.sub(h,1,4)
        h  = string.sub(h,5)
        table.insert(text,transform_table[h1])
    end
    return table.concat(text)
    
end

local function bintohex1 (h)

    local text = {}
    while h ~= '' do
        h1 = string.sub(h,1,4)
        h  = string.sub(h,5)
        table.insert(text,transform_table[h1])
    end
    return table.concat(text)
    
end

------------------------------------------------------------------------

local function hextobin (h)

    local text = {}
    while h ~= '' do
        h1 = string.sub(h,1,1)
        h  = string.sub(h,2)
        local x = transform_table[h1]
        table.insert(text,string.sub(x,1,1))
        table.insert(text,string.sub(x,2,2))
        table.insert(text,string.sub(x,3,3))
        table.insert(text,string.sub(x,4,4))
    end
    return text
    
end
    
------------------------------------------------------------------------

local function stohex(s, ln, sep)
    -- stohex(s [, ln [, sep)
    -- return the hex encoding of string s
    -- ln: (optional) a newline is inserted after 'ln' bytes
    --    ie. after 2*ln hex digits. Defaults to no newlines.
    -- sep: (optional) separator between bytes in the encoded string
    --    defaults to nothing (if ln is nil, sep is ignored)
    -- example:
    --    stohex('abcdef', 4, ':') => '61:62:63:64\n65:66'
    --    stohex('abcdef') => '616263646566'
    --
    if s == nil or #s == 0 then return '' end
    if not ln then -- no newline, no separator: do it the fast way!
        return (s:gsub('.',
            function(c) return strf('%02x', byte(c)) end
            ))
    end
    sep = sep or '' -- optional separator between each byte
    local t = {}
    for i = 1, #s - 1 do
        t[#t + 1] = strf('%02x%s', s:byte(i),
                (i % ln == 0) and '\n' or sep)
    end
    -- last byte, without any sep appended
    t[#t + 1] = strf('%02x', s:byte(#s))
    return concat(t)
end --stohex()

local function hextos(hs, unsafe)
    if #hs%2==1 then hs = "0" .. hs end
--    while string.sub(hs,1,2) == "00" do
--        hs = string.sub(hs,3)
--    end
    -- decode an hex encoded string. return the decoded string
    -- if optional parameter unsafe is defined, assume the hex
    -- string is well formed (no checks, no whitespace removal).
    -- Default is to remove white spaces (incl newlines)
    -- and check that the hex string is well formed
--    local tonumber = tonumber

--        local function tonumber1 (c, base)   --  Fehler in der urpsruenglichen LUA-Version auf dem Geraet
--                                             --  diese Funktion ist jetzt ersetzt durch die Funktion tonumber16
--            local nr = 0
--            while 0 == 0 do
--                nr = nr * 16
--                local c1 = string.sub(c,1,1)
--                if     c1 == 'a' or c1 == 'A' then nr = nr + 10
--                elseif c1 == 'b' or c1 == 'B' then nr = nr + 11
--                elseif c1 == 'c' or c1 == 'C' then nr = nr + 12
--                elseif c1 == 'd' or c1 == 'D' then nr = nr + 13
--                elseif c1 == 'e' or c1 == 'E' then nr = nr + 14
--                elseif c1 == 'f' or c1 == 'F' then nr = nr + 15
--                else nr = nr + tonumber(c1) end
--                if #c == 1 then
--                    break
--                end
--                c = string.sub(c,2)
--            end
--            return nr
--        end


--    if not unsafe then
--        hs = string.gsub(hs, '%s+', '') -- remove whitespaces
--        if string.find(hs, '[^0-9A-Za-z]') or #hs % 2 ~= 0 then
----            error('invalid hex string')
--            return('')
--        end
--    end
    return hs:gsub('(%x%x)', function(c)
                 return char( tonumber16(c, 16))
                 end) 
--        function(c) return string.char(tonumber(c, 16)) end
--        )
end -- hextos

local function rotr32(i, n)
    -- rotate right on 32 bits
    return ((i >> n) | (i << (32 - n))) & 0xffffffff
end

local function rotl32(i, n)
    -- rotate left on 32 bits
    return ((i << n) | (i >> (32 - n))) & 0xffffffff
end




local function xor1(key, plain)
    -- return a string which is a xor of plain and key
    -- plain and key may have arbitrary length.
    -- the result has the same length as plain.
    -- naive implementation, one byte at a time (ok for small strings)
    local ot = {}
    local ki, kln = 1, #key
    for i = 1, #plain do
        ot[#ot + 1] = char(byte(plain, i) ~ byte(key, ki))
        ki = ki + 1
        if ki > kln then ki = 1 end
    end
    return concat(ot)
end --xor1

local function xor8(key, plain)
    -- return a string which is a xor of plain and key
    -- plain may have arbitrary length.
    -- ** key length (in bytes) must be a multiple of 8 **
    -- the result has the same length as plain.
    -- (result is computed one uint64 at a time)
    assert(#key % 8 == 0, 'key not a multiple of 8 bytes')
    local ka = {} -- key as an array of uint64
    for i = 1, #key, 8 do
        -- !!beware below: () around sunpack are needed:
        -- app aka table.insert takes optionally 3 args
        -- and sunpack returns 2 args...
        app(ka, (sunpack('<I8', key, i)))
    end
    local kaln = #ka
    local rbn = #plain -- remaining bytes in plain
    local kai = 1  -- index in ka
    local ot = {}  -- table to collect output
    local ibu    -- an input block, as a uint64
    local ob    -- an output block as a string
    for i = 1, #plain, 8 do
        if rbn < 8 then
            local buffer = string.sub(plain, i) .. string.rep('\0', 8 - rbn)
            ibu = sunpack('<I8', buffer)
            ob = string.sub(spack('<I8', ibu ~ ka[kai]), 1, rbn)
        else
            ibu = sunpack('<I8', plain, i)
            ob = spack('<I8', ibu ~ ka[kai])
            rbn = rbn - 8
            kai = (kai < kaln) and (kai + 1) or 1
        end
        app(ot, ob)
    end
    return concat(ot)
end --xor8

------------------------------------------------------------------------
return  { -- bin module
    stohex = stohex,
    hextos = hextos,
    --
    rotr32 = rotr32,
    rotl32 = rotl32,
    --
    xor1 = xor1,
    xor8 = xor8,
    --
        hextobin     = hextobin,
        bintohex     = bintohex,
        bintohex1    = bintohex1,
    }

end

bin = preload_1009()

--*******************************************************************************

function tonumber16 (c, base)   --  replacement for tonumber(...,16)
        
    local nr = 0
    while 0 == 0 do
        nr = nr * 16
        local c1 = string.sub(c,1,1)
        if     c1 == 'a' or c1 == 'A' then nr = nr + 10
        elseif c1 == 'b' or c1 == 'B' then nr = nr + 11
        elseif c1 == 'c' or c1 == 'C' then nr = nr + 12
        elseif c1 == 'd' or c1 == 'D' then nr = nr + 13
        elseif c1 == 'e' or c1 == 'E' then nr = nr + 14
        elseif c1 == 'f' or c1 == 'F' then nr = nr + 15
        else nr = nr + tonumber(c1) end
        if #c == 1 then
            break
        end
        c = string.sub(c,2)
    end
    return nr

end

--*******************************************************************************

function tonumber2 (c, base)   --  replacement for tonumber(...,2)
        
    local nr = 0
    while 0 == 0 do
        nr = nr * 2
        local c1 = string.sub(c,1,1)
        nr = nr + tonumber(c1)
        if #c == 1 then
            break
        end
        c = string.sub(c,2)
    end
    return nr

end

--***************************************************************************************

---
-- An implementation of the lua 5.2 bit32 library, in pure Lua

-- Note that in Lua, "x % n" is defined such that will always return a number
-- between 0 and n-1 for positive n. We take advantage of that a lot here.

function checkint( name, argidx, x, level )
    local n = tonumber( x )
    if not n then
        error( string.format(
            "bad argument #%d to '%s' (number expected, got %s)",
            argidx, name, type( x )
        ), level + 1 )
    end
    return math.floor( n )
end

function checkint32( name, argidx, x, level )
    local n = tonumber( x )
    if not n then
        error( string.format(
            "bad argument #%d to '%s' (number expected, got %s)",
            argidx, name, type( x )
        ), level + 1 )
    end
    return math.floor( n ) % 0x100000000
end


function bit32_bnot( x )
    x = checkint32( 'bnot', 1, x, 2 )

    -- In two's complement, -x = not(x) + 1
    -- So not(x) = -x - 1
    return ( -x - 1 ) % 0x100000000
end


---
-- Logic tables for and/or/xor. We do pairs of bits here as a tradeoff between
-- table space and speed. If you change the number of bits, also change the
-- constants 2 and 4 in comb() below, and the initial value in bit32_band and
-- bit32_btest
logic_and = {
    [0] = { [0] = 0, 0, 0, 0},
    [1] = { [0] = 0, 1, 0, 1},
    [2] = { [0] = 0, 0, 2, 2},
    [3] = { [0] = 0, 1, 2, 3},
}
logic_or = {
    [0] = { [0] = 0, 1, 2, 3},
    [1] = { [0] = 1, 1, 3, 3},
    [2] = { [0] = 2, 3, 2, 3},
    [3] = { [0] = 3, 3, 3, 3},
}
logic_xor = {
    [0] = { [0] = 0, 1, 2, 3},
    [1] = { [0] = 1, 0, 3, 2},
    [2] = { [0] = 2, 3, 0, 1},
    [3] = { [0] = 3, 2, 1, 0},
}

---
-- @param name string Function name
-- @param args table Function args
-- @param nargs number Arg count
-- @param s number Start value, 0-3
-- @param t table Logic table
-- @return number result
function comb( name, args, nargs, s, t )
    for i = 1, nargs do
        args[i] = checkint32( name, i, args[i], 3 )
    end

    local pow = 1
    local ret = 0
    for b = 0, 31, 2 do
        local c = s
        for i = 1, nargs do
            c = t[c][args[i] % 4]
            args[i] = math.floor( args[i] / 4 )
        end
        ret = ret + c * pow
        pow = pow * 4
    end
    return ret
end

function bit32_band( ... )
    return comb( 'band', { ... }, select( '#', ... ), 3, logic_and )
end

function bit32_bor( ... )
    return comb( 'bor', { ... }, select( '#', ... ), 0, logic_or )
end

function bit32_bxor( ... )
    return comb( 'bxor', { ... }, select( '#', ... ), 0, logic_xor )
end

function bit32_btest( ... )
    return comb( 'btest', { ... }, select( '#', ... ), 3, logic_and ) ~= 0
end


function bit32_extract( n, field, width )
    n = checkint32( 'extract', 1, n, 2 )
    field = checkint( 'extract', 2, field, 2 )
    width = checkint( 'extract', 3, width or 1, 2 )
    if field < 0 then
        error( "bad argument #2 to 'extract' (field cannot be negative)", 2 )
    end
    if width <= 0 then
        error( "bad argument #3 to 'extract' (width must be positive)", 2 )
    end
    if field + width > 32 then
        error( 'trying to access non-existent bits', 2 )
    end

    return math.floor( n / 2^field ) % 2^width
end

function bit32_replace( n, v, field, width )
    n = checkint32( 'replace', 1, n, 2 )
    v = checkint32( 'replace', 2, v, 2 )
    field = checkint( 'replace', 3, field, 2 )
    width = checkint( 'replace', 4, width or 1, 2 )
    if field < 0 then
        error( "bad argument #3 to 'replace' (field cannot be negative)", 2 )
    end
    if width <= 0 then
        error( "bad argument #4 to 'replace' (width must be positive)", 2 )
    end
    if field + width > 32 then
        error( 'trying to access non-existent bits', 2 )
    end

    local f = 2^field
    local w = 2^width
    local fw = f * w
    return ( n % f ) + ( v % w ) * f + math.floor( n / fw ) * fw
end


-- For the shifting functions, anything over 32 is the same as 32
-- and limiting to 32 prevents overflow/underflow
function checkdisp( name, x )
    x = checkint( name, 2, x, 3 )
    return math.min( math.max( -32, x ), 32 )
end

function bit32_lshift( x, disp )
    x = checkint32( 'lshift', 1, x, 2 )
    disp = checkdisp( 'lshift', disp )

    return math.floor( x * 2^disp ) % 0x100000000
end

function bit32_rshift( x, disp )
    x = checkint32( 'rshift', 1, x, 2 )
    disp = checkdisp( 'rshift', disp )

    return math.floor( x / 2^disp ) % 0x100000000
end

function bit32_arshift( x, disp )
    x = checkint32( 'arshift', 1, x, 2 )
    disp = checkdisp( 'arshift', disp )

    if disp <= 0 then
        -- Non-positive displacement == left shift
        -- (since exponent is non-negative, the multipication can never result
        -- in a fractional part)
        return ( x * 2^-disp ) % 0x100000000
    elseif x < 0x80000000 then
        -- High bit is 0 == right shift
        -- (since exponent is positive, the division will never increase x)
        return math.floor( x / 2^disp )
    elseif disp > 31 then
        -- Shifting off all bits
        return 0xffffffff
    else
        -- 0x100000000 - 2 ^ ( 32 - disp ) creates a number with the high disp
        -- bits set. So shift right then add that number.
        return math.floor( x / 2^disp ) + ( 0x100000000 - 2 ^ ( 32 - disp ) )
    end
end

-- For the rotation functions, disp works mod 32.
-- Note that lrotate( x, disp ) == rrotate( x, -disp ).
function bit32_lrotate( x, disp )
    x = checkint32( 'lrotate', 1, x, 2 )
    disp = checkint( 'lrotate', 2, disp, 2 ) % 32

    local x = x * 2^disp
    return ( x % 0x100000000 ) + math.floor( x / 0x100000000 )
end

function bit32_rrotate( x, disp )
    x = checkint32( 'rrotate', 1, x, 2 )
    disp = -checkint( 'rrotate', 2, disp, 2 ) % 32

    local x = x * 2^disp
    return ( x % 0x100000000 ) + math.floor( x / 0x100000000 )
end


--*******************************************************************************


function preload_1015()


local cclxvi = {[0] = {0,0,0,0,0,0,0,0}, {1,0,0,0,0,0,0,0}, {0,1,0,0,0,0,0,0}, {1,1,0,0,0,0,0,0},
{0,0,1,0,0,0,0,0}, {1,0,1,0,0,0,0,0}, {0,1,1,0,0,0,0,0}, {1,1,1,0,0,0,0,0},
{0,0,0,1,0,0,0,0}, {1,0,0,1,0,0,0,0}, {0,1,0,1,0,0,0,0}, {1,1,0,1,0,0,0,0},
{0,0,1,1,0,0,0,0}, {1,0,1,1,0,0,0,0}, {0,1,1,1,0,0,0,0}, {1,1,1,1,0,0,0,0},
{0,0,0,0,1,0,0,0}, {1,0,0,0,1,0,0,0}, {0,1,0,0,1,0,0,0}, {1,1,0,0,1,0,0,0},
{0,0,1,0,1,0,0,0}, {1,0,1,0,1,0,0,0}, {0,1,1,0,1,0,0,0}, {1,1,1,0,1,0,0,0},
{0,0,0,1,1,0,0,0}, {1,0,0,1,1,0,0,0}, {0,1,0,1,1,0,0,0}, {1,1,0,1,1,0,0,0},
{0,0,1,1,1,0,0,0}, {1,0,1,1,1,0,0,0}, {0,1,1,1,1,0,0,0}, {1,1,1,1,1,0,0,0},
{0,0,0,0,0,1,0,0}, {1,0,0,0,0,1,0,0}, {0,1,0,0,0,1,0,0}, {1,1,0,0,0,1,0,0},
{0,0,1,0,0,1,0,0}, {1,0,1,0,0,1,0,0}, {0,1,1,0,0,1,0,0}, {1,1,1,0,0,1,0,0},
{0,0,0,1,0,1,0,0}, {1,0,0,1,0,1,0,0}, {0,1,0,1,0,1,0,0}, {1,1,0,1,0,1,0,0},
{0,0,1,1,0,1,0,0}, {1,0,1,1,0,1,0,0}, {0,1,1,1,0,1,0,0}, {1,1,1,1,0,1,0,0},
{0,0,0,0,1,1,0,0}, {1,0,0,0,1,1,0,0}, {0,1,0,0,1,1,0,0}, {1,1,0,0,1,1,0,0},
{0,0,1,0,1,1,0,0}, {1,0,1,0,1,1,0,0}, {0,1,1,0,1,1,0,0}, {1,1,1,0,1,1,0,0},
{0,0,0,1,1,1,0,0}, {1,0,0,1,1,1,0,0}, {0,1,0,1,1,1,0,0}, {1,1,0,1,1,1,0,0},
{0,0,1,1,1,1,0,0}, {1,0,1,1,1,1,0,0}, {0,1,1,1,1,1,0,0}, {1,1,1,1,1,1,0,0},
{0,0,0,0,0,0,1,0}, {1,0,0,0,0,0,1,0}, {0,1,0,0,0,0,1,0}, {1,1,0,0,0,0,1,0},
{0,0,1,0,0,0,1,0}, {1,0,1,0,0,0,1,0}, {0,1,1,0,0,0,1,0}, {1,1,1,0,0,0,1,0},
{0,0,0,1,0,0,1,0}, {1,0,0,1,0,0,1,0}, {0,1,0,1,0,0,1,0}, {1,1,0,1,0,0,1,0},
{0,0,1,1,0,0,1,0}, {1,0,1,1,0,0,1,0}, {0,1,1,1,0,0,1,0}, {1,1,1,1,0,0,1,0},
{0,0,0,0,1,0,1,0}, {1,0,0,0,1,0,1,0}, {0,1,0,0,1,0,1,0}, {1,1,0,0,1,0,1,0},
{0,0,1,0,1,0,1,0}, {1,0,1,0,1,0,1,0}, {0,1,1,0,1,0,1,0}, {1,1,1,0,1,0,1,0},
{0,0,0,1,1,0,1,0}, {1,0,0,1,1,0,1,0}, {0,1,0,1,1,0,1,0}, {1,1,0,1,1,0,1,0},
{0,0,1,1,1,0,1,0}, {1,0,1,1,1,0,1,0}, {0,1,1,1,1,0,1,0}, {1,1,1,1,1,0,1,0},
{0,0,0,0,0,1,1,0}, {1,0,0,0,0,1,1,0}, {0,1,0,0,0,1,1,0}, {1,1,0,0,0,1,1,0},
{0,0,1,0,0,1,1,0}, {1,0,1,0,0,1,1,0}, {0,1,1,0,0,1,1,0}, {1,1,1,0,0,1,1,0},
{0,0,0,1,0,1,1,0}, {1,0,0,1,0,1,1,0}, {0,1,0,1,0,1,1,0}, {1,1,0,1,0,1,1,0},
{0,0,1,1,0,1,1,0}, {1,0,1,1,0,1,1,0}, {0,1,1,1,0,1,1,0}, {1,1,1,1,0,1,1,0},
{0,0,0,0,1,1,1,0}, {1,0,0,0,1,1,1,0}, {0,1,0,0,1,1,1,0}, {1,1,0,0,1,1,1,0},
{0,0,1,0,1,1,1,0}, {1,0,1,0,1,1,1,0}, {0,1,1,0,1,1,1,0}, {1,1,1,0,1,1,1,0},
{0,0,0,1,1,1,1,0}, {1,0,0,1,1,1,1,0}, {0,1,0,1,1,1,1,0}, {1,1,0,1,1,1,1,0},
{0,0,1,1,1,1,1,0}, {1,0,1,1,1,1,1,0}, {0,1,1,1,1,1,1,0}, {1,1,1,1,1,1,1,0},
{0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,1}, {0,1,0,0,0,0,0,1}, {1,1,0,0,0,0,0,1},
{0,0,1,0,0,0,0,1}, {1,0,1,0,0,0,0,1}, {0,1,1,0,0,0,0,1}, {1,1,1,0,0,0,0,1},
{0,0,0,1,0,0,0,1}, {1,0,0,1,0,0,0,1}, {0,1,0,1,0,0,0,1}, {1,1,0,1,0,0,0,1},
{0,0,1,1,0,0,0,1}, {1,0,1,1,0,0,0,1}, {0,1,1,1,0,0,0,1}, {1,1,1,1,0,0,0,1},
{0,0,0,0,1,0,0,1}, {1,0,0,0,1,0,0,1}, {0,1,0,0,1,0,0,1}, {1,1,0,0,1,0,0,1},
{0,0,1,0,1,0,0,1}, {1,0,1,0,1,0,0,1}, {0,1,1,0,1,0,0,1}, {1,1,1,0,1,0,0,1},
{0,0,0,1,1,0,0,1}, {1,0,0,1,1,0,0,1}, {0,1,0,1,1,0,0,1}, {1,1,0,1,1,0,0,1},
{0,0,1,1,1,0,0,1}, {1,0,1,1,1,0,0,1}, {0,1,1,1,1,0,0,1}, {1,1,1,1,1,0,0,1},
{0,0,0,0,0,1,0,1}, {1,0,0,0,0,1,0,1}, {0,1,0,0,0,1,0,1}, {1,1,0,0,0,1,0,1},
{0,0,1,0,0,1,0,1}, {1,0,1,0,0,1,0,1}, {0,1,1,0,0,1,0,1}, {1,1,1,0,0,1,0,1},
{0,0,0,1,0,1,0,1}, {1,0,0,1,0,1,0,1}, {0,1,0,1,0,1,0,1}, {1,1,0,1,0,1,0,1},
{0,0,1,1,0,1,0,1}, {1,0,1,1,0,1,0,1}, {0,1,1,1,0,1,0,1}, {1,1,1,1,0,1,0,1},
{0,0,0,0,1,1,0,1}, {1,0,0,0,1,1,0,1}, {0,1,0,0,1,1,0,1}, {1,1,0,0,1,1,0,1},
{0,0,1,0,1,1,0,1}, {1,0,1,0,1,1,0,1}, {0,1,1,0,1,1,0,1}, {1,1,1,0,1,1,0,1},
{0,0,0,1,1,1,0,1}, {1,0,0,1,1,1,0,1}, {0,1,0,1,1,1,0,1}, {1,1,0,1,1,1,0,1},
{0,0,1,1,1,1,0,1}, {1,0,1,1,1,1,0,1}, {0,1,1,1,1,1,0,1}, {1,1,1,1,1,1,0,1},
{0,0,0,0,0,0,1,1}, {1,0,0,0,0,0,1,1}, {0,1,0,0,0,0,1,1}, {1,1,0,0,0,0,1,1},
{0,0,1,0,0,0,1,1}, {1,0,1,0,0,0,1,1}, {0,1,1,0,0,0,1,1}, {1,1,1,0,0,0,1,1},
{0,0,0,1,0,0,1,1}, {1,0,0,1,0,0,1,1}, {0,1,0,1,0,0,1,1}, {1,1,0,1,0,0,1,1},
{0,0,1,1,0,0,1,1}, {1,0,1,1,0,0,1,1}, {0,1,1,1,0,0,1,1}, {1,1,1,1,0,0,1,1},
{0,0,0,0,1,0,1,1}, {1,0,0,0,1,0,1,1}, {0,1,0,0,1,0,1,1}, {1,1,0,0,1,0,1,1},
{0,0,1,0,1,0,1,1}, {1,0,1,0,1,0,1,1}, {0,1,1,0,1,0,1,1}, {1,1,1,0,1,0,1,1},
{0,0,0,1,1,0,1,1}, {1,0,0,1,1,0,1,1}, {0,1,0,1,1,0,1,1}, {1,1,0,1,1,0,1,1},
{0,0,1,1,1,0,1,1}, {1,0,1,1,1,0,1,1}, {0,1,1,1,1,0,1,1}, {1,1,1,1,1,0,1,1},
{0,0,0,0,0,1,1,1}, {1,0,0,0,0,1,1,1}, {0,1,0,0,0,1,1,1}, {1,1,0,0,0,1,1,1},
{0,0,1,0,0,1,1,1}, {1,0,1,0,0,1,1,1}, {0,1,1,0,0,1,1,1}, {1,1,1,0,0,1,1,1},
{0,0,0,1,0,1,1,1}, {1,0,0,1,0,1,1,1}, {0,1,0,1,0,1,1,1}, {1,1,0,1,0,1,1,1},
{0,0,1,1,0,1,1,1}, {1,0,1,1,0,1,1,1}, {0,1,1,1,0,1,1,1}, {1,1,1,1,0,1,1,1},
{0,0,0,0,1,1,1,1}, {1,0,0,0,1,1,1,1}, {0,1,0,0,1,1,1,1}, {1,1,0,0,1,1,1,1},
{0,0,1,0,1,1,1,1}, {1,0,1,0,1,1,1,1}, {0,1,1,0,1,1,1,1}, {1,1,1,0,1,1,1,1},
{0,0,0,1,1,1,1,1}, {1,0,0,1,1,1,1,1}, {0,1,0,1,1,1,1,1}, {1,1,0,1,1,1,1,1},
{0,0,1,1,1,1,1,1}, {1,0,1,1,1,1,1,1}, {0,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1}}

-- Return a number that is the result of interpreting the table tbl (msb first)
local function tbl_to_number(tbl)
    local n = #tbl
    local rslt = 0
    local power = 1
    for i = 1, n do
        rslt = rslt + tbl[i]*power
        power = power*2
    end
    return rslt
end

-- Calculate bitwise xor of bytes m and n. 0 <= m,n <= 256.
local function bit_xor(m, n)
    local tbl_m = cclxvi[m]
    local tbl_n = cclxvi[n]
    local tbl = {}
    for i = 1, 8 do
        if(tbl_m[i] ~= tbl_n[i]) then
            tbl[i] = 1
        else
            tbl[i] = 0
        end
    end
    return tbl_to_number(tbl)
end

-- Return the binary representation of the number x with the width of `digits`.
local function binary(x,digits)
  local s=string.format("%o",x)
  local a={["0"]="000",["1"]="001", ["2"]="010",["3"]="011",
           ["4"]="100",["5"]="101", ["6"]="110",["7"]="111"}
  s=string.gsub(s,"(.)",function (d) return a[d] end)
  -- remove leading 0s
  s = string.gsub(s,"^0*(.*)$","%1")
  local fmtstring = string.format("%%%ds",digits)
  local ret = string.format(fmtstring,s)
  return string.gsub(ret," ","0")
end

-- A small helper function for add_typeinfo_to_matrix() and add_version_information()
-- Add a 2 (black by default) / -2 (blank by default) to the matrix at position x,y
-- depending on the bitstring (size 1!) where "0"=blank and "1"=black.
local function fill_matrix_position(matrix,bitstring,x,y)
    if bitstring == "1" then
        matrix[x][y] = 2
    else
        matrix[x][y] = -2
    end
end


--- Step 1: Determine version, ec level and mode for codeword
--- ========================================================
---
--- First we need to find out the version (= size) of the QR code. This depends on
--- the input data (the mode to be used), the requested error correction level
--- (normally we use the maximum level that fits into the minimal size).

-- Return the mode for the given string `str`.
-- See table 2 of the spec. We only support mode 1, 2 and 4.
-- That is: numeric, alaphnumeric and binary.
local function get_mode( str )
    if string.match(str,"^[0-9]+$") then
        return 1
    elseif string.match(str,"^[0-9A-Z $%%*./:+-]+$") then
        return 2
    else
        return 4
    end
    assert(false,"never reached") -- luacheck: ignore
    return nil
end



--- Capacity of QR codes
--- --------------------
--- The capacity is calculated as follow: \\(\text{Number of data bits} = \text{number of codewords} * 8\\).
--- The number of data bits is now reduced by 4 (the mode indicator) and the length string,
--- that varies between 8 and 16, depending on the version and the mode (see method `get_length()`). The
--- remaining capacity is multiplied by the amount of data per bit string (numeric: 3, alphanumeric: 2, other: 1)
--- and divided by the length of the bit string (numeric: 10, alphanumeric: 11, binary: 8, kanji: 13).
--- Then the floor function is applied to the result:
--- $$\Big\lfloor \frac{( \text{#data bits} - 4 - \text{length string}) * \text{data per bit string}}{\text{length of the bit string}} \Big\rfloor$$
---
--- There is one problem remaining. The length string depends on the version,
--- and the version depends on the length string. But we take this into account when calculating the
--- the capacity, so this is not really a problem here.

-- The capacity (number of codewords) of each version (1-40) for error correction levels 1-4 (LMQH).
-- The higher the ec level, the lower the capacity of the version. Taken from spec, tables 7-11.
local capacity = {
  {  19,   16,   13,    9},{  34,   28,   22,   16},{  55,   44,   34,   26},{  80,   64,   48,   36},
  { 108,   86,   62,   46},{ 136,  108,   76,   60},{ 156,  124,   88,   66},{ 194,  154,  110,   86},
  { 232,  182,  132,  100},{ 274,  216,  154,  122},{ 324,  254,  180,  140},{ 370,  290,  206,  158},
  { 428,  334,  244,  180},{ 461,  365,  261,  197},{ 523,  415,  295,  223},{ 589,  453,  325,  253},
  { 647,  507,  367,  283},{ 721,  563,  397,  313},{ 795,  627,  445,  341},{ 861,  669,  485,  385},
  { 932,  714,  512,  406},{1006,  782,  568,  442},{1094,  860,  614,  464},{1174,  914,  664,  514},
  {1276, 1000,  718,  538},{1370, 1062,  754,  596},{1468, 1128,  808,  628},{1531, 1193,  871,  661},
  {1631, 1267,  911,  701},{1735, 1373,  985,  745},{1843, 1455, 1033,  793},{1955, 1541, 1115,  845},
  {2071, 1631, 1171,  901},{2191, 1725, 1231,  961},{2306, 1812, 1286,  986},{2434, 1914, 1354, 1054},
  {2566, 1992, 1426, 1096},{2702, 2102, 1502, 1142},{2812, 2216, 1582, 1222},{2956, 2334, 1666, 1276}}


--- Return the smallest version for this codeword. If `requested_ec_level` is supplied,
--- then the ec level (LMQH - 1,2,3,4) must be at least the requested level.
-- mode = 1,2,4,8
local function get_version_eclevel(len,mode,requested_ec_level)
    local local_mode = mode
    if mode == 4 then
        local_mode = 3
    elseif mode == 8 then
        local_mode = 4
    end
    assert( local_mode <= 4 )

    local bits, digits, modebits, c
    local tab = { {10,9,8,8},{12,11,16,10},{14,13,16,12} }
    local minversion = 40
    local maxec_level = requested_ec_level or 1
    local min,max = 1, 4
    if requested_ec_level and requested_ec_level >= 1 and requested_ec_level <= 4 then
        min = requested_ec_level
        max = requested_ec_level
    end
    for ec_level=min,max do
        for version=1,#capacity do
            bits = capacity[version][ec_level] * 8
            bits = bits - 4 -- the mode indicator
            if version < 10 then
                digits = tab[1][local_mode]
            elseif version < 27 then
                digits = tab[2][local_mode]
            elseif version <= 40 then
                digits = tab[3][local_mode]
            end
            modebits = bits - digits
            if local_mode == 1 then -- numeric
                c = math.floor(modebits * 3 / 10)
            elseif local_mode == 2 then -- alphanumeric
                c = math.floor(modebits * 2 / 11)
            elseif local_mode == 3 then -- binary
                c = math.floor(modebits * 1 / 8)
            else
                c = math.floor(modebits * 1 / 13)
            end
            if c >= len then
                if version <= minversion then
                    minversion = version
                    maxec_level = ec_level
                end
                break
            end
        end
    end
    return minversion, maxec_level
end

-- Return a bit string of 0s and 1s that includes the length of the code string.
-- The modes are numeric = 1, alphanumeric = 2, binary = 4, and japanese = 8
local function get_length(str,version,mode)
    local i = mode
    if mode == 4 then
        i = 3
    elseif mode == 8 then
        i = 4
    end
    assert( i <= 4 )
    local tab = { {10,9,8,8},{12,11,16,10},{14,13,16,12} }
    local digits
    if version < 10 then
        digits = tab[1][i]
    elseif version < 27 then
        digits = tab[2][i]
    elseif version <= 40 then
        digits = tab[3][i]
    else
        assert(false, "get_length, version > 40 not supported")
    end
    local len = binary(#str,digits)
    return len
end

--- If the `requested_ec_level` or the `mode` are provided, this will be used if possible.
--- The mode depends on the characters used in the string `str`. It seems to be
--- possible to split the QR code to handle multiple modes, but we don't do that.
local function get_version_eclevel_mode_bistringlength(str,requested_ec_level,mode)
    local local_mode
    if mode then
        assert(false,"not implemented")
        -- check if the mode is OK for the string
        local_mode = mode
    else
        local_mode = get_mode(str)
    end
    local version, ec_level
    version, ec_level = get_version_eclevel(#str,local_mode,requested_ec_level)
    local length_string = get_length(str,version,local_mode)
    return version,ec_level,binary(local_mode,4),local_mode,length_string
end

--- Step 2: Encode data
--- ===================

--- There are several ways to encode the data. We currently support only numeric, alphanumeric and binary.
--- We already chose the encoding (a.k.a. mode) in the first step, so we need to apply the mode to the
--- codeword.
---
--- **Numeric**: take three digits and encode them in 10 bits
--- **Alphanumeric**: take two characters and encode them in 11 bits
--- **Binary**: take one octet and encode it in 8 bits

local asciitbl = {
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  -- 0x01-0x0f
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  -- 0x10-0x1f
    36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,  -- 0x20-0x2f
     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 44, -1, -1, -1, -1, -1,  -- 0x30-0x3f
    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,  -- 0x40-0x4f
    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,  -- 0x50-0x5f
  }

-- Return a binary representation of the numeric string `str`. This must contain only digits 0-9.
local function encode_string_numeric(str)
    local bitstring = ""
    local int
    string.gsub(str,"..?.?",function(a)
        int = tonumber(a)
        if #a == 3 then
            bitstring = bitstring .. binary(int,10)
        elseif #a == 2 then
            bitstring = bitstring .. binary(int,7)
        else
            bitstring = bitstring .. binary(int,4)
        end
    end)
    return bitstring
end

-- Return a binary representation of the alphanumeric string `str`. This must contain only
-- digits 0-9, uppercase letters A-Z, space and the following chars: $%*./:+-.
local function encode_string_ascii(str)
    local bitstring = ""
    local int
    local b1, b2
    string.gsub(str,"..?",function(a)
        if #a == 2 then
            b1 = asciitbl[string.byte(string.sub(a,1,1))]
            b2 = asciitbl[string.byte(string.sub(a,2,2))]
            int = b1 * 45 + b2
            bitstring = bitstring .. binary(int,11)
        else
            int = asciitbl[string.byte(a)]
            bitstring = bitstring .. binary(int,6)
        end
      end)
    return bitstring
end

-- Return a bitstring representing string str in binary mode.
-- We don't handle UTF-8 in any special way because we assume the
-- scanner recognizes UTF-8 and displays it correctly.
local function encode_string_binary(str)
    local ret = {}
    string.gsub(str,".",function(x)
        ret[#ret + 1] = binary(string.byte(x),8)
    end)
    return table.concat(ret)
end

-- Return a bitstring representing string str in the given mode.
local function encode_data(str,mode)
    if mode == 1 then
        return encode_string_numeric(str)
    elseif mode == 2 then
        return encode_string_ascii(str)
    elseif mode == 4 then
        return encode_string_binary(str)
    else
        assert(false,"not implemented yet")
    end
end

-- Encoding the codeword is not enough. We need to make sure that
-- the length of the binary string is equal to the number of codewords of the version.
local function add_pad_data(version,ec_level,data)
    local count_to_pad, missing_digits
    local cpty = capacity[version][ec_level] * 8
    count_to_pad = math.min(4,cpty - #data)
    if count_to_pad > 0 then
        data = data .. string.rep("0",count_to_pad)
    end
    if math.fmod(#data,8) ~= 0 then
        missing_digits = 8 - math.fmod(#data,8)
        data = data .. string.rep("0",missing_digits)
    end
    assert(math.fmod(#data,8) == 0)
    -- add "11101100" and "00010001" until enough data
    while #data < cpty do
        data = data .. "11101100"
        if #data < cpty then
            data = data .. "00010001"
        end
    end
    return data
end



--- Step 3: Organize data and calculate error correction code
--- =======================================================
--- The data in the qrcode is not encoded linearly. For example code 5-H has four blocks, the first two blocks
--- contain 11 codewords and 22 error correction codes each, the second block contain 12 codewords and 22 ec codes each.
--- We just take the table from the spec and don't calculate the blocks ourself. The table `ecblocks` contains this info.
---
--- During the phase of splitting the data into codewords, we do the calculation for error correction codes. This step involves
--- polynomial division. Find a math book from school and follow the code here :)

--- ### Reed Solomon error correction
--- Now this is the slightly ugly part of the error correction. We start with log/antilog tables
-- https://codyplanteen.com/assets/rs/gf256_log_antilog.pdf
local alpha_int = {
    [0] = 1,
      2,   4,   8,  16,  32,  64, 128,  29,  58, 116, 232, 205, 135,  19,  38,  76,
    152,  45,  90, 180, 117, 234, 201, 143,   3,   6,  12,  24,  48,  96, 192, 157,
     39,  78, 156,  37,  74, 148,  53, 106, 212, 181, 119, 238, 193, 159,  35,  70,
    140,   5,  10,  20,  40,  80, 160,  93, 186, 105, 210, 185, 111, 222, 161,  95,
    190,  97, 194, 153,  47,  94, 188, 101, 202, 137,  15,  30,  60, 120, 240, 253,
    231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163,  91, 182, 113, 226, 217,
    175,  67, 134,  17,  34,  68, 136,  13,  26,  52, 104, 208, 189, 103, 206, 129,
     31,  62, 124, 248, 237, 199, 147,  59, 118, 236, 197, 151,  51, 102, 204, 133,
     23,  46,  92, 184, 109, 218, 169,  79, 158,  33,  66, 132,  21,  42,  84, 168,
     77, 154,  41,  82, 164,  85, 170,  73, 146,  57, 114, 228, 213, 183, 115, 230,
    209, 191,  99, 198, 145,  63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227,
    219, 171,  75, 150,  49,  98, 196, 149,  55, 110, 220, 165,  87, 174,  65, 130,
     25,  50, 100, 200, 141,   7,  14,  28,  56, 112, 224, 221, 167,  83, 166,  81,
    162,  89, 178, 121, 242, 249, 239, 195, 155,  43,  86, 172,  69, 138,   9,  18,
     36,  72, 144,  61, 122, 244, 245, 247, 243, 251, 235, 203, 139,  11,  22,  44,
     88, 176, 125, 250, 233, 207, 131,  27,  54, 108, 216, 173,  71, 142,   0,   0
}

local int_alpha = {
    [0] = 256, -- special value
    0,   1,  25,   2,  50,  26, 198,   3, 223,  51, 238,  27, 104, 199,  75,   4,
    100, 224,  14,  52, 141, 239, 129,  28, 193, 105, 248, 200,   8,  76, 113,   5,
    138, 101,  47, 225,  36,  15,  33,  53, 147, 142, 218, 240,  18, 130,  69,  29,
    181, 194, 125, 106,  39, 249, 185, 201, 154,   9, 120,  77, 228, 114, 166,   6,
    191, 139,  98, 102, 221,  48, 253, 226, 152,  37, 179,  16, 145,  34, 136,  54,
    208, 148, 206, 143, 150, 219, 189, 241, 210,  19,  92, 131,  56,  70,  64,  30,
     66, 182, 163, 195,  72, 126, 110, 107,  58,  40,  84, 250, 133, 186,  61, 202,
     94, 155, 159,  10,  21, 121,  43,  78, 212, 229, 172, 115, 243, 167,  87,   7,
    112, 192, 247, 140, 128,  99,  13, 103,  74, 222, 237,  49, 197, 254,  24, 227,
    165, 153, 119,  38, 184, 180, 124,  17,  68, 146, 217,  35,  32, 137,  46,  55,
     63, 209,  91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190,  97, 242,
     86, 211, 171,  20,  42,  93, 158, 132,  60,  57,  83,  71, 109,  65, 162,  31,
     45,  67, 216, 183, 123, 164, 118, 196,  23,  73, 236, 127,  12, 111, 246, 108,
    161,  59,  82,  41, 157,  85, 170, 251,  96, 134, 177, 187, 204,  62,  90, 203,
     89,  95, 176, 156, 169, 160,  81,  11, 245,  22, 235, 122, 117,  44, 215,  79,
    174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168,  80,  88, 175
}

-- We only need the polynomial generators for block sizes 7, 10, 13, 15, 16, 17, 18, 20, 22, 24, 26, 28, and 30. Version
-- 2 of the qr codes don't need larger ones (as opposed to version 1). The table has the format x^1*ɑ^21 + x^2*a^102 ...
local generator_polynomial = {
     [7] = { 21, 102, 238, 149, 146, 229,  87,   0},
    [10] = { 45,  32,  94,  64,  70, 118,  61,  46,  67, 251,   0 },
    [13] = { 78, 140, 206, 218, 130, 104, 106, 100,  86, 100, 176, 152,  74,   0 },
    [15] = {105,  99,   5, 124, 140, 237,  58,  58,  51,  37, 202,  91,  61, 183,   8,   0},
    [16] = {120, 225, 194, 182, 169, 147, 191,  91,   3,  76, 161, 102, 109, 107, 104, 120,   0},
    [17] = {136, 163, 243,  39, 150,  99,  24, 147, 214, 206, 123, 239,  43,  78, 206, 139,  43,   0},
    [18] = {153,  96,  98,   5, 179, 252, 148, 152, 187,  79, 170, 118,  97, 184,  94, 158, 234, 215,   0},
    [20] = {190, 188, 212, 212, 164, 156, 239,  83, 225, 221, 180, 202, 187,  26, 163,  61,  50,  79,  60,  17,   0},
    [22] = {231, 165, 105, 160, 134, 219,  80,  98, 172,   8,  74, 200,  53, 221, 109,  14, 230,  93, 242, 247, 171, 210,   0},
    [24] = { 21, 227,  96,  87, 232, 117,   0, 111, 218, 228, 226, 192, 152, 169, 180, 159, 126, 251, 117, 211,  48, 135, 121, 229,   0},
    [26] = { 70, 218, 145, 153, 227,  48, 102,  13, 142, 245,  21, 161,  53, 165,  28, 111, 201, 145,  17, 118, 182, 103,   2, 158, 125, 173,   0},
    [28] = {123,   9,  37, 242, 119, 212, 195,  42,  87, 245,  43,  21, 201, 232,  27, 205, 147, 195, 190, 110, 180, 108, 234, 224, 104, 200, 223, 168,   0},
    [30] = {180, 192,  40, 238, 216, 251,  37, 156, 130, 224, 193, 226, 173,  42, 125, 222,  96, 239,  86, 110,  48,  50, 182, 179,  31, 216, 152, 145, 173, 41, 0}}


-- Turn a binary string of length 8*x into a table size x of numbers.
local function convert_bitstring_to_bytes(data)
    local msg = {}
    string.gsub(data,"(........)",function(x)
        msg[#msg+1] = tonumber2(x,2)
    end)
    return msg
end

-- Return a table that has 0's in the first entries and then the alpha
-- representation of the generator polynominal
local function get_generator_polynominal_adjusted(num_ec_codewords,highest_exponent)
    local gp_alpha = {[0]=0}
    for i=0,highest_exponent - num_ec_codewords - 1 do
        gp_alpha[i] = 0
    end
    local gp = generator_polynomial[num_ec_codewords]
    for i=1,num_ec_codewords + 1 do
        gp_alpha[highest_exponent - num_ec_codewords + i - 1] = gp[i]
    end
    return gp_alpha
end

--- These converter functions use the log/antilog table above.
--- We could have created the table programatically, but I like fixed tables.
-- Convert polynominal in int notation to alpha notation.
local function convert_to_alpha( tab )
    local new_tab = {}
    for i=0,#tab do
        new_tab[i] = int_alpha[tab[i]]
    end
    return new_tab
end

-- Convert polynominal in alpha notation to int notation.
local function convert_to_int(tab)
    local new_tab = {}
    for i=0,#tab do
        new_tab[i] = alpha_int[tab[i]]
    end
    return new_tab
end

-- That's the heart of the error correction calculation.
local function calculate_error_correction(data,num_ec_codewords)
    local mp
    if type(data)=="string" then
        mp = convert_bitstring_to_bytes(data)
    elseif type(data)=="table" then
        mp = data
    else
        assert(false,string.format("Unknown type for data: %s",type(data)))
    end
    local len_message = #mp

    local highest_exponent = len_message + num_ec_codewords - 1
    local gp_alpha,tmp
    local he
    local gp_int, mp_alpha
    local mp_int = {}
    -- create message shifted to left (highest exponent)
    for i=1,len_message do
        mp_int[highest_exponent - i + 1] = mp[i]
    end
    for i=1,highest_exponent - len_message do
        mp_int[i] = 0
    end
    mp_int[0] = 0

    mp_alpha = convert_to_alpha(mp_int)

    local zaehler = 0
    while highest_exponent >= num_ec_codewords do
        feed_the_dog()
        zaehler = zaehler + 1
        if zaehler % 3 == 0 and CALCULATE_QRCODE_FOR_REPORT == 1 then
            if file_open ~= nil then
                start_timer(26,WAIT_26,0,1)
                error2("qrcode_0001")
                coroutine.yield()
             else
                notify("QR - Error correction, WAIT26")
             end
        end
        gp_alpha = get_generator_polynominal_adjusted(num_ec_codewords,highest_exponent)

        -- Multiply generator polynomial by first coefficient of the above polynomial

        -- take the highest exponent from the message polynom (alpha) and add
        -- it to the generator polynom
        local exp = mp_alpha[highest_exponent]
        for i=highest_exponent,highest_exponent - num_ec_codewords,-1 do
            if exp ~= 256 then
                if gp_alpha[i] + exp >= 255 then
                    gp_alpha[i] = math.fmod(gp_alpha[i] + exp,255)
                else
                    gp_alpha[i] = gp_alpha[i] + exp
                end
            else
                gp_alpha[i] = 256
            end
        end
        for i=highest_exponent - num_ec_codewords - 1,0,-1 do
            gp_alpha[i] = 256
        end

        gp_int = convert_to_int(gp_alpha)
        mp_int = convert_to_int(mp_alpha)


        tmp = {}
        for i=highest_exponent,0,-1 do
            tmp[i] = bit_xor(gp_int[i],mp_int[i])
        end
        -- remove leading 0's
        he = highest_exponent
        for i=he,0,-1 do
            -- We need to stop if the length of the codeword is matched
            if i < num_ec_codewords then break end
            if tmp[i] == 0 then
                tmp[i] = nil
                highest_exponent = highest_exponent - 1
            else
                break
            end
        end
        mp_int = tmp
        mp_alpha = convert_to_alpha(mp_int)
    end
    local ret = {}

    -- reverse data
    for i=#mp_int,0,-1 do
        ret[#ret + 1] = mp_int[i]
    end
    return ret
end

--- #### Arranging the data
--- Now we arrange the data into smaller chunks. This table is taken from the spec.
-- ecblocks has 40 entries, one for each version. Each version entry has 4 entries, for each LMQH
-- ec level. Each entry has two or four fields, the odd files are the number of repetitions for the
-- folowing block info. The first entry of the block is the total number of codewords in the block,
-- the second entry is the number of data codewords. The third is not important.
local ecblocks = {
  {{  1,{ 26, 19, 2}                 },   {  1,{26,16, 4}},                  {  1,{26,13, 6}},                  {  1, {26, 9, 8}               }},
  {{  1,{ 44, 34, 4}                 },   {  1,{44,28, 8}},                  {  1,{44,22,11}},                  {  1, {44,16,14}               }},
  {{  1,{ 70, 55, 7}                 },   {  1,{70,44,13}},                  {  2,{35,17, 9}},                  {  2, {35,13,11}               }},
  {{  1,{100, 80,10}                 },   {  2,{50,32, 9}},                  {  2,{50,24,13}},                  {  4, {25, 9, 8}               }},
  {{  1,{134,108,13}                 },   {  2,{67,43,12}},                  {  2,{33,15, 9},  2,{34,16, 9}},   {  2, {33,11,11},  2,{34,12,11}}},
  {{  2,{ 86, 68, 9}                 },   {  4,{43,27, 8}},                  {  4,{43,19,12}},                  {  4, {43,15,14}               }},
  {{  2,{ 98, 78,10}                 },   {  4,{49,31, 9}},                  {  2,{32,14, 9},  4,{33,15, 9}},   {  4, {39,13,13},  1,{40,14,13}}},
  {{  2,{121, 97,12}                 },   {  2,{60,38,11},  2,{61,39,11}},   {  4,{40,18,11},  2,{41,19,11}},   {  4, {40,14,13},  2,{41,15,13}}},
  {{  2,{146,116,15}                 },   {  3,{58,36,11},  2,{59,37,11}},   {  4,{36,16,10},  4,{37,17,10}},   {  4, {36,12,12},  4,{37,13,12}}},
  {{  2,{ 86, 68, 9},  2,{ 87, 69, 9}},   {  4,{69,43,13},  1,{70,44,13}},   {  6,{43,19,12},  2,{44,20,12}},   {  6, {43,15,14},  2,{44,16,14}}},
  {{  4,{101, 81,10}                 },   {  1,{80,50,15},  4,{81,51,15}},   {  4,{50,22,14},  4,{51,23,14}},   {  3, {36,12,12},  8,{37,13,12}}},
  {{  2,{116, 92,12},  2,{117, 93,12}},   {  6,{58,36,11},  2,{59,37,11}},   {  4,{46,20,13},  6,{47,21,13}},   {  7, {42,14,14},  4,{43,15,14}}},
  {{  4,{133,107,13}                 },   {  8,{59,37,11},  1,{60,38,11}},   {  8,{44,20,12},  4,{45,21,12}},   { 12, {33,11,11},  4,{34,12,11}}},
  {{  3,{145,115,15},  1,{146,116,15}},   {  4,{64,40,12},  5,{65,41,12}},   { 11,{36,16,10},  5,{37,17,10}},   { 11, {36,12,12},  5,{37,13,12}}},
  {{  5,{109, 87,11},  1,{110, 88,11}},   {  5,{65,41,12},  5,{66,42,12}},   {  5,{54,24,15},  7,{55,25,15}},   { 11, {36,12,12},  7,{37,13,12}}},
  {{  5,{122, 98,12},  1,{123, 99,12}},   {  7,{73,45,14},  3,{74,46,14}},   { 15,{43,19,12},  2,{44,20,12}},   {  3, {45,15,15}, 13,{46,16,15}}},
  {{  1,{135,107,14},  5,{136,108,14}},   { 10,{74,46,14},  1,{75,47,14}},   {  1,{50,22,14}, 15,{51,23,14}},   {  2, {42,14,14}, 17,{43,15,14}}},
  {{  5,{150,120,15},  1,{151,121,15}},   {  9,{69,43,13},  4,{70,44,13}},   { 17,{50,22,14},  1,{51,23,14}},   {  2, {42,14,14}, 19,{43,15,14}}},
  {{  3,{141,113,14},  4,{142,114,14}},   {  3,{70,44,13}, 11,{71,45,13}},   { 17,{47,21,13},  4,{48,22,13}},   {  9, {39,13,13}, 16,{40,14,13}}},
  {{  3,{135,107,14},  5,{136,108,14}},   {  3,{67,41,13}, 13,{68,42,13}},   { 15,{54,24,15},  5,{55,25,15}},   { 15, {43,15,14}, 10,{44,16,14}}},
  {{  4,{144,116,14},  4,{145,117,14}},   { 17,{68,42,13}},                  { 17,{50,22,14},  6,{51,23,14}},   { 19, {46,16,15},  6,{47,17,15}}},
  {{  2,{139,111,14},  7,{140,112,14}},   { 17,{74,46,14}},                  {  7,{54,24,15}, 16,{55,25,15}},   { 34, {37,13,12}               }},
  {{  4,{151,121,15},  5,{152,122,15}},   {  4,{75,47,14}, 14,{76,48,14}},   { 11,{54,24,15}, 14,{55,25,15}},   { 16, {45,15,15}, 14,{46,16,15}}},
  {{  6,{147,117,15},  4,{148,118,15}},   {  6,{73,45,14}, 14,{74,46,14}},   { 11,{54,24,15}, 16,{55,25,15}},   { 30, {46,16,15},  2,{47,17,15}}},
  {{  8,{132,106,13},  4,{133,107,13}},   {  8,{75,47,14}, 13,{76,48,14}},   {  7,{54,24,15}, 22,{55,25,15}},   { 22, {45,15,15}, 13,{46,16,15}}},
  {{ 10,{142,114,14},  2,{143,115,14}},   { 19,{74,46,14},  4,{75,47,14}},   { 28,{50,22,14},  6,{51,23,14}},   { 33, {46,16,15},  4,{47,17,15}}},
  {{  8,{152,122,15},  4,{153,123,15}},   { 22,{73,45,14},  3,{74,46,14}},   {  8,{53,23,15}, 26,{54,24,15}},   { 12, {45,15,15}, 28,{46,16,15}}},
  {{  3,{147,117,15}, 10,{148,118,15}},   {  3,{73,45,14}, 23,{74,46,14}},   {  4,{54,24,15}, 31,{55,25,15}},   { 11, {45,15,15}, 31,{46,16,15}}},
  {{  7,{146,116,15},  7,{147,117,15}},   { 21,{73,45,14},  7,{74,46,14}},   {  1,{53,23,15}, 37,{54,24,15}},   { 19, {45,15,15}, 26,{46,16,15}}},
  {{  5,{145,115,15}, 10,{146,116,15}},   { 19,{75,47,14}, 10,{76,48,14}},   { 15,{54,24,15}, 25,{55,25,15}},   { 23, {45,15,15}, 25,{46,16,15}}},
  {{ 13,{145,115,15},  3,{146,116,15}},   {  2,{74,46,14}, 29,{75,47,14}},   { 42,{54,24,15},  1,{55,25,15}},   { 23, {45,15,15}, 28,{46,16,15}}},
  {{ 17,{145,115,15}                 },   { 10,{74,46,14}, 23,{75,47,14}},   { 10,{54,24,15}, 35,{55,25,15}},   { 19, {45,15,15}, 35,{46,16,15}}},
  {{ 17,{145,115,15},  1,{146,116,15}},   { 14,{74,46,14}, 21,{75,47,14}},   { 29,{54,24,15}, 19,{55,25,15}},   { 11, {45,15,15}, 46,{46,16,15}}},
  {{ 13,{145,115,15},  6,{146,116,15}},   { 14,{74,46,14}, 23,{75,47,14}},   { 44,{54,24,15},  7,{55,25,15}},   { 59, {46,16,15},  1,{47,17,15}}},
  {{ 12,{151,121,15},  7,{152,122,15}},   { 12,{75,47,14}, 26,{76,48,14}},   { 39,{54,24,15}, 14,{55,25,15}},   { 22, {45,15,15}, 41,{46,16,15}}},
  {{  6,{151,121,15}, 14,{152,122,15}},   {  6,{75,47,14}, 34,{76,48,14}},   { 46,{54,24,15}, 10,{55,25,15}},   {  2, {45,15,15}, 64,{46,16,15}}},
  {{ 17,{152,122,15},  4,{153,123,15}},   { 29,{74,46,14}, 14,{75,47,14}},   { 49,{54,24,15}, 10,{55,25,15}},   { 24, {45,15,15}, 46,{46,16,15}}},
  {{  4,{152,122,15}, 18,{153,123,15}},   { 13,{74,46,14}, 32,{75,47,14}},   { 48,{54,24,15}, 14,{55,25,15}},   { 42, {45,15,15}, 32,{46,16,15}}},
  {{ 20,{147,117,15},  4,{148,118,15}},   { 40,{75,47,14},  7,{76,48,14}},   { 43,{54,24,15}, 22,{55,25,15}},   { 10, {45,15,15}, 67,{46,16,15}}},
  {{ 19,{148,118,15},  6,{149,119,15}},   { 18,{75,47,14}, 31,{76,48,14}},   { 34,{54,24,15}, 34,{55,25,15}},   { 20, {45,15,15}, 61,{46,16,15}}}
}

-- The bits that must be 0 if the version does fill the complete matrix.
-- Example: for version 1, no bits need to be added after arranging the data, for version 2 we need to add 7 bits at the end.
local remainder = {0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0}

-- This is the formula for table 1 in the spec:
-- function get_capacity_remainder( version )
--     local len = version * 4 + 17
--     local size = len^2
--     local function_pattern_modules = 192 + 2 * len - 32 -- Position Adjustment pattern + timing pattern
--     local count_alignemnt_pattern = #alignment_pattern[version]
--     if count_alignemnt_pattern > 0 then
--         -- add 25 for each aligment pattern
--         function_pattern_modules = function_pattern_modules + 25 * ( count_alignemnt_pattern^2 - 3 )
--         -- but substract the timing pattern occupied by the aligment pattern on the top and left
--         function_pattern_modules = function_pattern_modules - ( count_alignemnt_pattern - 2) * 10
--     end
--     size = size - function_pattern_modules
--     if version > 6 then
--         size = size - 67
--     else
--         size = size - 31
--     end
--     return math.floor(size/8),math.fmod(size,8)
-- end


--- Example: Version 5-H has four data and four error correction blocks. The table above lists
--- `2, {33,11,11},  2,{34,12,11}` for entry [5][4]. This means we take two blocks with 11 codewords
--- and two blocks with 12 codewords, and two blocks with 33 - 11 = 22 ec codes and another
--- two blocks with 34 - 12 = 22 ec codes.
---         Block 1: D1  D2  D3  ... D11
---         Block 2: D12 D13 D14 ... D22
---         Block 3: D23 D24 D25 ... D33 D34
---         Block 4: D35 D36 D37 ... D45 D46
--- Then we place the data like this in the matrix: D1, D12, D23, D35, D2, D13, D24, D36 ... D45, D34, D46.  The same goes
--- with error correction codes.

-- The given data can be a string of 0's and 1' (with #string mod 8 == 0).
-- Alternatively the data can be a table of codewords. The number of codewords
-- must match the capacity of the qr code.
local function arrange_codewords_and_calculate_ec( version,ec_level,data )
    if type(data)=="table" then
        local tmp = ""
        for i=1,#data do
            tmp = tmp .. binary(data[i],8)
        end
        data = tmp
    end
    -- If the size of the data is not enough for the codeword, we add 0's and two special bytes until finished.
    local blocks = ecblocks[version][ec_level]
    local size_datablock_bytes, size_ecblock_bytes
    local datablocks = {}
    local final_ecblocks = {}
    local count = 1
    local pos = 0
    local cpty_ec_bits = 0
    for i=1,#blocks/2 do
        for _=1,blocks[2*i - 1] do
                feed_the_dog()
                if CALCULATE_QRCODE_FOR_REPORT == 1 then
                    if file_open ~= nil then
                        start_timer(26,WAIT_26,0,1)
                        error2("qrcode_0002")
                        coroutine.yield()
                    else
                        notify("QR Code - Codewords, WAIT26")
                    end
                end
            size_datablock_bytes = blocks[2*i][2]
            size_ecblock_bytes   = blocks[2*i][1] - blocks[2*i][2]
            cpty_ec_bits = cpty_ec_bits + size_ecblock_bytes * 8
            datablocks[#datablocks + 1] = string.sub(data, pos * 8 + 1,( pos + size_datablock_bytes)*8)
            local tmp_tab = calculate_error_correction(datablocks[#datablocks],size_ecblock_bytes)
            local tmp_str = ""
            for x=1,#tmp_tab do
                tmp_str = tmp_str .. binary(tmp_tab[x],8)
            end
            final_ecblocks[#final_ecblocks + 1] = tmp_str
            pos = pos + size_datablock_bytes
            count = count + 1
        end
    end
    local arranged_data = ""
    pos = 1
    repeat
        for i=1,#datablocks do
            if pos < #datablocks[i] then
                arranged_data = arranged_data .. string.sub(datablocks[i],pos, pos + 7)
            end
        end
        pos = pos + 8
    until #arranged_data == #data
    -- ec
    local arranged_ec = ""
    pos = 1
    repeat
        for i=1,#final_ecblocks do
            if pos < #final_ecblocks[i] then
                arranged_ec = arranged_ec .. string.sub(final_ecblocks[i],pos, pos + 7)
            end
        end
        pos = pos + 8
    until #arranged_ec == cpty_ec_bits
    return arranged_data .. arranged_ec
end

--- Step 4: Generate 8 matrices with different masks and calculate the penalty
--- ==========================================================================
---
--- Prepare matrix
--- --------------
--- The first step is to prepare an _empty_ matrix for a given size/mask. The matrix has a
--- few predefined areas that must be black or blank. We encode the matrix with a two
--- dimensional field where the numbers determine which pixel is blank or not.
---
--- The following code is used for our matrix:
---         0 = not in use yet,
---        -2 = blank by mandatory pattern,
---         2 = black by mandatory pattern,
---        -1 = blank by data,
---         1 = black by data
---
---
--- To prepare the _empty_, we add positioning, alingment and timing patters.

--- ### Positioning patterns ###
local function add_position_detection_patterns(tab_x)
    local size = #tab_x
    -- allocate quite zone in the matrix area
    for i=1,8 do
        for j=1,8 do
            tab_x[i][j] = -2
            tab_x[size - 8 + i][j] = -2
            tab_x[i][size - 8 + j] = -2
        end
    end
    -- draw the detection pattern (outer)
    for i=1,7 do
        -- top left
        tab_x[1][i]=2
        tab_x[7][i]=2
        tab_x[i][1]=2
        tab_x[i][7]=2

        -- top right
        tab_x[size][i]=2
        tab_x[size - 6][i]=2
        tab_x[size - i + 1][1]=2
        tab_x[size - i + 1][7]=2

        -- bottom left
        tab_x[1][size - i + 1]=2
        tab_x[7][size - i + 1]=2
        tab_x[i][size - 6]=2
        tab_x[i][size]=2
    end
    -- draw the detection pattern (inner)
    for i=1,3 do
        for j=1,3 do
            -- top left
            tab_x[2+j][i+2]=2
            -- top right
            tab_x[size - j - 1][i+2]=2
            -- bottom left
            tab_x[2 + j][size - i - 1]=2
        end
    end
end

--- ### Timing patterns ###
-- The timing patterns (two) are the dashed lines between two adjacent positioning patterns on row/column 7.
local function add_timing_pattern(tab_x)
    local line,col
    line = 7
    col = 9
    for i=col,#tab_x - 8 do
        if math.fmod(i,2) == 1 then
            tab_x[i][line] = 2
        else
            tab_x[i][line] = -2
        end
    end
    for i=col,#tab_x - 8 do
        if math.fmod(i,2) == 1 then
            tab_x[line][i] = 2
        else
            tab_x[line][i] = -2
        end
    end
end


--- ### Alignment patterns ###
--- The alignment patterns must be added to the matrix for versions > 1. The amount and positions depend on the versions and are
--- given by the spec. Beware: the patterns must not be placed where we have the positioning patterns
--- (that is: top left, top right and bottom left.)

-- For each version, where should we place the alignment patterns? See table E.1 of the spec
local alignment_pattern = {
  {},{6,18},{6,22},{6,26},{6,30},{6,34}, -- 1-6
  {6,22,38},{6,24,42},{6,26,46},{6,28,50},{6,30,54},{6,32,58},{6,34,62}, -- 7-13
  {6,26,46,66},{6,26,48,70},{6,26,50,74},{6,30,54,78},{6,30,56,82},{6,30,58,86},{6,34,62,90}, -- 14-20
  {6,28,50,72,94},{6,26,50,74,98},{6,30,54,78,102},{6,28,54,80,106},{6,32,58,84,110},{6,30,58,86,114},{6,34,62,90,118}, -- 21-27
  {6,26,50,74,98 ,122},{6,30,54,78,102,126},{6,26,52,78,104,130},{6,30,56,82,108,134},{6,34,60,86,112,138},{6,30,58,86,114,142},{6,34,62,90,118,146}, -- 28-34
  {6,30,54,78,102,126,150}, {6,24,50,76,102,128,154},{6,28,54,80,106,132,158},{6,32,58,84,110,136,162},{6,26,54,82,110,138,166},{6,30,58,86,114,142,170} -- 35 - 40
}

--- The alignment pattern has size 5x5 and looks like this:
---     XXXXX
---     X   X
---     X X X
---     X   X
---     XXXXX
local function add_alignment_pattern( tab_x )
    local version = (#tab_x - 17) / 4
    local ap = alignment_pattern[version]
    local pos_x, pos_y
    for x=1,#ap do
        for y=1,#ap do
            -- we must not put an alignment pattern on top of the positioning pattern
            if not (x == 1 and y == 1 or x == #ap and y == 1 or x == 1 and y == #ap ) then
                pos_x = ap[x] + 1
                pos_y = ap[y] + 1
                tab_x[pos_x][pos_y] = 2
                tab_x[pos_x+1][pos_y] = -2
                tab_x[pos_x-1][pos_y] = -2
                tab_x[pos_x+2][pos_y] =  2
                tab_x[pos_x-2][pos_y] =  2
                tab_x[pos_x  ][pos_y - 2] = 2
                tab_x[pos_x+1][pos_y - 2] = 2
                tab_x[pos_x-1][pos_y - 2] = 2
                tab_x[pos_x+2][pos_y - 2] = 2
                tab_x[pos_x-2][pos_y - 2] = 2
                tab_x[pos_x  ][pos_y + 2] = 2
                tab_x[pos_x+1][pos_y + 2] = 2
                tab_x[pos_x-1][pos_y + 2] = 2
                tab_x[pos_x+2][pos_y + 2] = 2
                tab_x[pos_x-2][pos_y + 2] = 2

                tab_x[pos_x  ][pos_y - 1] = -2
                tab_x[pos_x+1][pos_y - 1] = -2
                tab_x[pos_x-1][pos_y - 1] = -2
                tab_x[pos_x+2][pos_y - 1] =  2
                tab_x[pos_x-2][pos_y - 1] =  2
                tab_x[pos_x  ][pos_y + 1] = -2
                tab_x[pos_x+1][pos_y + 1] = -2
                tab_x[pos_x-1][pos_y + 1] = -2
                tab_x[pos_x+2][pos_y + 1] =  2
                tab_x[pos_x-2][pos_y + 1] =  2
            end
        end
    end
end

--- ### Type information ###
--- Let's not forget the type information that is in column 9 next to the left positioning patterns and on row 9 below
--- the top positioning patterns. This type information is not fixed, it depends on the mask and the error correction.

-- The first index is ec level (LMQH,1-4), the second is the mask (0-7). This bitstring of length 15 is to be used
-- as mandatory pattern in the qrcode. Mask -1 is for debugging purpose only and is the 'noop' mask.
local typeinfo = {
    { [-1]= "111111111111111", [0] = "111011111000100", "111001011110011", "111110110101010", "111100010011101", "110011000101111", "110001100011000", "110110001000001", "110100101110110" },
    { [-1]= "111111111111111", [0] = "101010000010010", "101000100100101", "101111001111100", "101101101001011", "100010111111001", "100000011001110", "100111110010111", "100101010100000" },
    { [-1]= "111111111111111", [0] = "011010101011111", "011000001101000", "011111100110001", "011101000000110", "010010010110100", "010000110000011", "010111011011010", "010101111101101" },
    { [-1]= "111111111111111", [0] = "001011010001001", "001001110111110", "001110011100111", "001100111010000", "000011101100010", "000001001010101", "000110100001100", "000100000111011" }
}

-- The typeinfo is a mixture of mask and ec level information and is
-- added twice to the qr code, one horizontal, one vertical.
local function add_typeinfo_to_matrix( matrix,ec_level,mask )
    local ec_mask_type = typeinfo[ec_level][mask]

    local bit
    -- vertical from bottom to top
    for i=1,7 do
        bit = string.sub(ec_mask_type,i,i)
        fill_matrix_position(matrix, bit, 9, #matrix - i + 1)
    end
    for i=8,9 do
        bit = string.sub(ec_mask_type,i,i)
        fill_matrix_position(matrix,bit,9,17-i)
    end
    for i=10,15 do
        bit = string.sub(ec_mask_type,i,i)
        fill_matrix_position(matrix,bit,9,16 - i)
    end
    -- horizontal, left to right
    for i=1,6 do
        bit = string.sub(ec_mask_type,i,i)
        fill_matrix_position(matrix,bit,i,9)
    end
    bit = string.sub(ec_mask_type,7,7)
    fill_matrix_position(matrix,bit,8,9)
    for i=8,15 do
        bit = string.sub(ec_mask_type,i,i)
        fill_matrix_position(matrix,bit,#matrix - 15 + i,9)
    end
end

-- Bits for version information 7-40
-- The reversed strings from https://www.thonky.com/qr-code-tutorial/format-version-tables
local version_information = {"001010010011111000", "001111011010000100", "100110010101100100", "110010110010010100",
  "011011111101110100", "010001101110001100", "111000100001101100", "101100000110011100", "000101001001111100",
  "000111101101000010", "101110100010100010", "111010000101010010", "010011001010110010", "011001011001001010",
  "110000010110101010", "100100110001011010", "001101111110111010", "001000110111000110", "100001111000100110",
  "110101011111010110", "011100010000110110", "010110000011001110", "111111001100101110", "101011101011011110",
  "000010100100111110", "101010111001000001", "000011110110100001", "010111010001010001", "111110011110110001",
  "110100001101001001", "011101000010101001", "001001100101011001", "100000101010111001", "100101100011000101" }

-- Versions 7 and above need two bitfields with version information added to the code
local function add_version_information(matrix,version)
    if version < 7 then return end
    local size = #matrix
    local bitstring = version_information[version - 6]
    local x,y, bit
    local start_x, start_y
    -- first top right
    start_x = size - 10
    start_y = 1
    for i=1,#bitstring do
        bit = string.sub(bitstring,i,i)
        x = start_x + math.fmod(i - 1,3)
        y = start_y + math.floor( (i - 1) / 3 )
        fill_matrix_position(matrix,bit,x,y)
    end

    -- now bottom left
    start_x = 1
    start_y = size - 10
    for i=1,#bitstring do
        bit = string.sub(bitstring,i,i)
        x = start_x + math.floor( (i - 1) / 3 )
        y = start_y + math.fmod(i - 1,3)
        fill_matrix_position(matrix,bit,x,y)
    end
end

--- Now it's time to use the methods above to create a prefilled matrix for the given mask
local function prepare_matrix_with_mask( version,ec_level, mask )
    local size
    local tab_x = {}

    size = version * 4 + 17
    for i=1,size do
        tab_x[i]={}
        for j=1,size do
            tab_x[i][j] = 0
        end
    end
    add_position_detection_patterns(tab_x)
    add_timing_pattern(tab_x)
    add_version_information(tab_x,version)

    -- black pixel above lower left position detection pattern
    tab_x[9][size - 7] = 2
    add_alignment_pattern(tab_x)
    add_typeinfo_to_matrix(tab_x,ec_level, mask)
    return tab_x
end

--- Finally we come to the place where we need to put the calculated data (remember step 3?) into the qr code.
--- We do this for each mask. BTW speaking of mask, this is what we find in the spec:
---         Mask Pattern Reference   Condition
---         000                      (y + x) mod 2 = 0
---         001                      y mod 2 = 0
---         010                      x mod 3 = 0
---         011                      (y + x) mod 3 = 0
---         100                      ((y div 2) + (x div 3)) mod 2 = 0
---         101                      (y x) mod 2 + (y x) mod 3 = 0
---         110                      ((y x) mod 2 + (y x) mod 3) mod 2 = 0
---         111                      ((y x) mod 3 + (y+x) mod 2) mod 2 = 0

-- Return 1 (black) or -1 (blank) depending on the mask, value and position.
-- Parameter mask is 0-7 (-1 for 'no mask'). x and y are 1-based coordinates,
-- 1,1 = upper left. tonumber(value) must be 0 or 1.
local function get_pixel_with_mask( mask, x,y,value )
    x = x - 1
    y = y - 1
    local invert = false
    -- test purpose only:
    if mask == -1 then -- luacheck: ignore
        -- ignore, no masking applied
    elseif mask == 0 then
        if math.fmod(x + y,2) == 0 then invert = true end
    elseif mask == 1 then
        if math.fmod(y,2) == 0 then invert = true end
    elseif mask == 2 then
        if math.fmod(x,3) == 0 then invert = true end
    elseif mask == 3 then
        if math.fmod(x + y,3) == 0 then invert = true end
    elseif mask == 4 then
        if math.fmod(math.floor(y / 2) + math.floor(x / 3),2) == 0 then invert = true end
    elseif mask == 5 then
        if math.fmod(x * y,2) + math.fmod(x * y,3) == 0 then invert = true end
    elseif mask == 6 then
        if math.fmod(math.fmod(x * y,2) + math.fmod(x * y,3),2) == 0 then invert = true end
    elseif mask == 7 then
        if math.fmod(math.fmod(x * y,3) + math.fmod(x + y,2),2) == 0 then invert = true end
    else
        assert(false,"This can't happen (mask must be <= 7)")
    end
    if invert then
        -- value = 1? -> -1, value = 0? -> 1
        return 1 - 2 * tonumber(value)
    else
        -- value = 1? -> 1, value = 0? -> -1
        return -1 + 2*tonumber(value)
    end
end


-- We need up to 8 positions in the matrix. Only the last few bits may be less then 8.
-- The function returns a table of (up to) 8 entries with subtables where
-- the x coordinate is the first and the y coordinate is the second entry.
local function get_next_free_positions(matrix,x,y,dir,byte)
    local ret = {}
    local count = 1
    local mode = "right"
    while count <= #byte do
        if mode == "right" and matrix[x][y] == 0 then
            ret[#ret + 1] = {x,y}
            mode = "left"
            count = count + 1
        elseif mode == "left" and matrix[x-1][y] == 0 then
            ret[#ret + 1] = {x-1,y}
            mode = "right"
            count = count + 1
            if dir == "up" then
                y = y - 1
            else
                y = y + 1
            end
        elseif mode == "right" and matrix[x-1][y] == 0 then
            ret[#ret + 1] = {x-1,y}
            count = count + 1
            if dir == "up" then
                y = y - 1
            else
                y = y + 1
            end
        else
            if dir == "up" then
                y = y - 1
            else
                y = y + 1
            end
        end
        if y < 1 or y > #matrix then
            x = x - 2
            -- don't overwrite the timing pattern
            if x == 7 then x = 6 end
            if dir == "up" then
                dir = "down"
                y = 1
            else
                dir = "up"
                y = #matrix
            end
        end
    end
    return ret,x,y,dir
end

-- Add the data string (0's and 1's) to the matrix for the given mask.
local function add_data_to_matrix(matrix,data,mask)
    local a = 1
    local size = #matrix
    local x,y,positions
    local _x,_y,m
    local dir = "up"
    local byte_number = 0
    x,y = size,size
    print ("DATALENGTH",#data)



  if (1==1) then


    while (0 == 0) do

        local byte = string.sub(data,byte_number*8+1,byte_number*8+8)
        if byte == "" then
            break
        end
        byte_number       = byte_number + 1
        
--        if byte_number > 10 then
--            return
--        end
        
        if (byte_number%20 == 0) and (CALCULATE_QRCODE_FOR_REPORT == 1) then
            if file_open ~= nil then
                start_timer(26,WAIT_26,0,1)
                error2("qrcode_0003")
                coroutine.yield()
             else
                notify("QRCode ADD_DATA_TO_MATRIX - byte_number: " .. tostring(byte_number) .. " WAIT26")
             end
        end
        positions,x,y,dir = get_next_free_positions(matrix,x,y,dir,byte)
        for i=1,#byte do  --  das letzte Byte hat ggfs. keine 8 bits
            _x = positions[i][1]
            _y = positions[i][2]
            m = get_pixel_with_mask(mask,_x,_y,string.sub(byte,i,i))
            if debugging then
                matrix[_x][_y] = m * (i + 10)
            else
                matrix[_x][_y] = m
            end
        end

    end
 
 end

 if (1==0) then

    string.gsub(data,".?.?.?.?.?.?.?.?",function ( byte )   --  originaler Code, aber sehr umstaendlich. Geht ueber gsub,
        byte_number = byte_number + 1                       --  welches einen "Cursor" zur Verfuegung stellen soll.
        positions,x,y,dir = get_next_free_positions(matrix,x,y,dir,byte)   --  Muss besser straightforward gemacht werden, s.o.
        for i=1,#byte do   --  i geht immer von 1 bis 8
            _x = positions[i][1]
            _y = positions[i][2]
            m = get_pixel_with_mask(mask,_x,_y,string.sub(byte,i,i))
            if debugging then
                matrix[_x][_y] = m * (i + 10)
            else
                matrix[_x][_y] = m
            end
        end
    end)

 end


end


--- The total penalty of the matrix is the sum of four steps. The following steps are taken into account:
---
--- 1. Adjacent modules in row/column in same color
--- 1. Block of modules in same color
--- 1. 1:1:3:1:1 ratio (dark:light:dark:light:dark) pattern in row/column
--- 1. Proportion of dark modules in entire symbol
---
--- This all is done to avoid bad patterns in the code that prevent the scanner from
--- reading the code.
-- Return the penalty for the given matrix
local function calculate_penalty(matrix)
    local penalty1, penalty2, penalty3 = 0,0,0
    local size = #matrix
    -- this is for penalty 4
    local number_of_dark_cells = 0

    -- 1: Adjacent modules in row/column in same color
    -- --------------------------------------------
    -- No. of modules = (5+i)  -> 3 + i
    local last_bit_blank -- < 0:  blank, > 0: black
    local is_blank
    local number_of_consecutive_bits
    -- first: vertical
    for x=1,size do
        number_of_consecutive_bits = 0
        last_bit_blank = nil
        for y = 1,size do
            if matrix[x][y] > 0 then
                -- small optimization: this is for penalty 4
                number_of_dark_cells = number_of_dark_cells + 1
                is_blank = false
            else
                is_blank = true
            end
            if last_bit_blank == is_blank then
                number_of_consecutive_bits = number_of_consecutive_bits + 1
            else
                if number_of_consecutive_bits >= 5 then
                    penalty1 = penalty1 + number_of_consecutive_bits - 2
                end
                number_of_consecutive_bits = 1
            end
            last_bit_blank = is_blank
        end
        if number_of_consecutive_bits >= 5 then
            penalty1 = penalty1 + number_of_consecutive_bits - 2
        end
    end
    -- now horizontal
    for y=1,size do
        number_of_consecutive_bits = 0
        last_bit_blank = nil
        for x = 1,size do
            is_blank = matrix[x][y] < 0
            if last_bit_blank == is_blank then
                number_of_consecutive_bits = number_of_consecutive_bits + 1
            else
                if number_of_consecutive_bits >= 5 then
                    penalty1 = penalty1 + number_of_consecutive_bits - 2
                end
                number_of_consecutive_bits = 1
            end
            last_bit_blank = is_blank
        end
        if number_of_consecutive_bits >= 5 then
            penalty1 = penalty1 + number_of_consecutive_bits - 2
        end
    end
    for x=1,size do
        for y=1,size do
            -- 2: Block of modules in same color
            -- -----------------------------------
            -- Blocksize = m × n  -> 3 × (m-1) × (n-1)
            if (y < size - 1) and ( x < size - 1) and ( (matrix[x][y] < 0 and matrix[x+1][y] < 0 and matrix[x][y+1] < 0 and matrix[x+1][y+1] < 0) or (matrix[x][y] > 0 and matrix[x+1][y] > 0 and matrix[x][y+1] > 0 and matrix[x+1][y+1] > 0) ) then
                penalty2 = penalty2 + 3
            end

            -- 3: 1:1:3:1:1 ratio (dark:light:dark:light:dark) pattern in row/column
            -- ------------------------------------------------------------------
            -- Gives 40 points each
            --
            -- I have no idea why we need the extra 0000 on left or right side. The spec doesn't mention it,
            -- other sources do mention it. This is heavily inspired by zxing.
            if (y + 6 < size and
                matrix[x][y] > 0 and
                matrix[x][y +  1] < 0 and
                matrix[x][y +  2] > 0 and
                matrix[x][y +  3] > 0 and
                matrix[x][y +  4] > 0 and
                matrix[x][y +  5] < 0 and
                matrix[x][y +  6] > 0 and
                ((y + 10 < size and
                    matrix[x][y +  7] < 0 and
                    matrix[x][y +  8] < 0 and
                    matrix[x][y +  9] < 0 and
                    matrix[x][y + 10] < 0) or
                 (y - 4 >= 1 and
                    matrix[x][y -  1] < 0 and
                    matrix[x][y -  2] < 0 and
                    matrix[x][y -  3] < 0 and
                    matrix[x][y -  4] < 0))) then penalty3 = penalty3 + 40 end
            if (x + 6 <= size and
                matrix[x][y] > 0 and
                matrix[x +  1][y] < 0 and
                matrix[x +  2][y] > 0 and
                matrix[x +  3][y] > 0 and
                matrix[x +  4][y] > 0 and
                matrix[x +  5][y] < 0 and
                matrix[x +  6][y] > 0 and
                ((x + 10 <= size and
                    matrix[x +  7][y] < 0 and
                    matrix[x +  8][y] < 0 and
                    matrix[x +  9][y] < 0 and
                    matrix[x + 10][y] < 0) or
                 (x - 4 >= 1 and
                    matrix[x -  1][y] < 0 and
                    matrix[x -  2][y] < 0 and
                    matrix[x -  3][y] < 0 and
                    matrix[x -  4][y] < 0))) then penalty3 = penalty3 + 40 end
        end
    end
    -- 4: Proportion of dark modules in entire symbol
    -- ----------------------------------------------
    -- 50 ± (5 × k)% to 50 ± (5 × (k + 1))% -> 10 × k
    local dark_ratio = number_of_dark_cells / ( size * size )
    local penalty4 = math.floor(math.abs(dark_ratio * 100 - 50)) * 2
    return penalty1 + penalty2 + penalty3 + penalty4
end

-- Create a matrix for the given parameters and calculate the penalty score.
-- Return both (matrix and penalty)
local function get_matrix_and_penalty(version,ec_level,data,mask)

        if CALCULATE_QRCODE_FOR_REPORT == 1 then
            if file_open ~= nil then
                start_timer(26,WAIT_26,0,1)
                error2("qrcode_0004")
                coroutine.yield()
             else
                notify("QR - GET_MATRIX_AND_PENALTY, WAIT26")
             end
        end


    local tab = prepare_matrix_with_mask(version,ec_level,mask)
    add_data_to_matrix(tab,data,mask)
    local penalty = calculate_penalty(tab)
    return tab, penalty
--    return tab, 100
end

-- Return the matrix with the smallest penalty. To to this
-- we try out the matrix for all 8 masks and determine the
-- penalty (score) each.
local function get_matrix_with_lowest_penalty(version,ec_level,data)
    local tab, penalty
    local tab_min_penalty, min_penalty

    -- try masks 0-7
    tab_min_penalty, min_penalty = get_matrix_and_penalty(version,ec_level,data,0)
    for i=1,7 do
        tab, penalty = get_matrix_and_penalty(version,ec_level,data,i)
        if penalty < min_penalty then
            tab_min_penalty = tab
            min_penalty = penalty
        end
    end
    return tab_min_penalty
end

--- The main function. We connect everything together. Remember from above:
---
--- 1. Determine version, ec level and mode (=encoding) for codeword
--- 1. Encode data
--- 1. Arrange data and calculate error correction code
--- 1. Generate 8 matrices with different masks and calculate the penalty
--- 1. Return qrcode with least penalty
-- If ec_level or mode is given, use the ones for generating the qrcode. (mode is not implemented yet)
local function qrcode( str, ec_level, _mode ) -- luacheck: no unused args
    local arranged_data, version, data_raw, mode, len_bitstring
    version, ec_level, data_raw, mode, len_bitstring = get_version_eclevel_mode_bistringlength(str,ec_level)
    data_raw = data_raw .. len_bitstring
    
        if CALCULATE_QRCODE_FOR_REPORT == 1 then
            if file_open ~= nil then
                start_timer(26,WAIT_26,0,1)
                error2("qrcode_0004")
                coroutine.yield()
             else
                notify("QRCode 1, WAIT26")
             end
        end

    
    data_raw = data_raw .. encode_data(str,mode)

        if CALCULATE_QRCODE_FOR_REPORT == 1 then
            if file_open ~= nil then
                start_timer(26,WAIT_26,0,1)
                error2("qrcode_0005")
                coroutine.yield()
             else
                notify("QRCode 2, WAIT26")
             end
        end

    data_raw = add_pad_data(version,ec_level,data_raw)

        if CALCULATE_QRCODE_FOR_REPORT == 1 then
            if file_open ~= nil then
                start_timer(26,WAIT_26,0,1)
                error2("qrcode_0006")
                coroutine.yield()
             else
                notify("QRCode 3, WAIT26")
             end
        end

    arranged_data = arrange_codewords_and_calculate_ec(version,ec_level,data_raw)


        if CALCULATE_QRCODE_FOR_REPORT == 1 then
            if file_open ~= nil then
                start_timer(26,WAIT_26,0,1)
                error2("qrcode_0007")
                coroutine.yield()
             else
                notify("QRCode 4, WAIT26")
             end
        end

    


    if math.fmod(#arranged_data,8) ~= 0 then
        return false, string.format("Arranged data %% 8 != 0: data length = %d, mod 8 = %d",#arranged_data, math.fmod(#arranged_data,8))
    end
    arranged_data = arranged_data .. string.rep("0",remainder[version])

        if CALCULATE_QRCODE_FOR_REPORT == 1 then
            if file_open ~= nil then
                start_timer(26,WAIT_26,0,1)
                error2("qrcode_0008")
                coroutine.yield()
             else
                notify("QRCode 5, WAIT26")
             end
        end

--  bis hierher ohne Abbruch

    
    local tab = get_matrix_with_lowest_penalty(version,ec_level,arranged_data)


    if (1==0) then
        return true, {}
    end 



--        if CALCULATE_QRCODE_FOR_REPORT == 1 then
--            if file_open ~= nil then
--                start_timer(26,WAIT_26,0,1)
--                coroutine.yield()
--             else
--                notify("QRCode 6, WAIT26")
--             end
--        end
    return true, tab
end


if testing then
    return {
        encode_string_numeric = encode_string_numeric,
        encode_string_ascii = encode_string_ascii,
        qrcode = qrcode,
        binary = binary,
        get_mode = get_mode,
        get_length = get_length,
        add_pad_data = add_pad_data,
        get_generator_polynominal_adjusted = get_generator_polynominal_adjusted,
        get_pixel_with_mask = get_pixel_with_mask,
        get_version_eclevel_mode_bistringlength = get_version_eclevel_mode_bistringlength,
        remainder = remainder,
        --get_capacity_remainder = get_capacity_remainder,
        arrange_codewords_and_calculate_ec = arrange_codewords_and_calculate_ec,
        calculate_error_correction = calculate_error_correction,
        convert_bitstring_to_bytes = convert_bitstring_to_bytes,
        bit_xor = bit_xor,
    }
end

return {
    qrcode = qrcode
}

end

qrc = preload_1015()


--*************************************************************************

function preload_1016()

----------------------------------------------------------------------------------
--
-- LibCompress.lua
--
-- Authors: jjsheets and Galmok of European Stormrage (Horde)
-- Email : sheets.jeff@gmail.com and galmok@gmail.com
-- Licence: GPL version 2 (General Public License)
----------------------------------------------------------------------------------

-- Modified for use in OpenComputers
-- By: SpiritedDusty

local LibCompress = {}

-- local is faster than global
local type = type
local select = select
local next = next
local loadstring = loadstring
local setmetatable = setmetatable
local assert = assert
local table_insert = table.insert
local table_remove = table.remove
local table_concat = table.concat
local string_char = string.char
local string_byte = string.byte
local string_len = string.len
local string_sub = string.sub
local unpack = unpack
local pairs = pairs
local math_modf = math.modf
local bit_band = bit32_band
local bit_bor = bit32_bor
local bit_bxor = bit32_bxor
local bit_bnot = bit32_bnot
local bit_lshift = bit32_lshift
local bit_rshift = bit32_rshift

--------------------------------------------------------------------------------
-- Cleanup

local tables = {} -- tables that may be cleaned have to be kept here
local tables_to_clean = {} -- list of tables by name (string) that may be reset to {} after a timeout
local timeout = -1

-- tables that may be erased
local function cleanup()
    for k,v in pairs(tables_to_clean) do
        tables[k]={}
        tables_to_clean[k]=nil
    end
end

local function onUpdate(frame, elapsed)
    timeout = timeout - elapsed
    if timeout < 0 then
        frame:Hide()
        cleanup()
    end
end

local function setCleanupTables(...)
    timeout = 15 -- empty tables after 15 seconds
    for i=1,select("#",...) do
        tables_to_clean[(select(i, ...))] = true
    end
end



----------------------------------------------------------------------
----------------------------------------------------------------------
--
-- compression algorithms

--------------------------------------------------------------------------------
-- LZW codec
-- implemented by sheets.jeff@gmail.com

-- encode is used to uniquely encode a number into a sequence of bytes that can be decoded using decode()
-- the bytes returned by this do not contain "\000"
local bytes = {}
local function encode(x)
    for k = 1, #bytes do bytes[k] = nil end
    local xmod
    x, xmod = math_modf(x/255)
    xmod = xmod * 255
    bytes[#bytes + 1] = xmod
    while x > 0 do
        x, xmod = math_modf(x/255)
        xmod = xmod * 255
        bytes[#bytes + 1] = xmod
    end
    if #bytes == 1 and bytes[1] > 0 and bytes[1] < 250 then
        return string_char(bytes[1])
    else
        for i = 1, #bytes do bytes[i] = bytes[i] + 1 end
        return string_char(256 - #bytes, math.floor(table.unpack(bytes)))
    end
end

--decode converts a unique character sequence into its equivalent number, from ss, beginning at the ith char.
-- returns the decoded number and the count of characters used in the decode process.
local function decode(ss,i)
    i = i or 1
    local a = string_byte(ss,i,i)
    if a > 249 then
        local r = 0
        a = 256 - a
        for n = i+a, i+1, -1 do
            r = r * 255 + string_byte(ss,n,n) - 1
        end
        return r, a + 1
    else
        return a, 1
    end
end

-- Compresses the given uncompressed string.
-- Unless the uncompressed string starts with "\002", this is guaranteed to return a string equal to or smaller than
-- the passed string.
-- the returned string will only contain "\000" characters in rare circumstances, and will contain none if the
-- source string has none.
local dict = {}
function LibCompress:CompressLZW(uncompressed)
    if type(uncompressed) == "string" then
        local dict_size = 256
        for k in pairs(dict) do
            dict[k] = nil
        end
        local result = {"\002"}
        local w = ''
        local ressize = 1
        for i = 0, 255 do
            dict[string_char(i)] = i
        end
        for i = 1, #uncompressed do
            local c = uncompressed:sub(i,i)
            local wc = w..c
            if dict[wc] then
                w = wc
            else
                dict[wc] = dict_size
                dict_size = dict_size +1
                local r = encode(dict[w])
                ressize = ressize + #r
                result[#result + 1] = r
                w = c
            end
        end
        if w then
            local r = encode(dict[w])
            ressize = ressize + #r
            result[#result + 1] = r
        end
        if (#uncompressed+1) > ressize then
            return table_concat(result)
        else
            return string_char(1)..uncompressed
        end
    else
        return nil, "Can only compress strings"
    end
end

-- if the passed string is a compressed string, this will decompress it and return the decompressed string.
-- Otherwise it return an error message
-- compressed strings are marked by beginning with "\002"
function LibCompress:DecompressLZW(compressed)
    if type(compressed) == "string" then
        if compressed:sub(1,1) ~= "\002" then
            return nil, "Can only decompress LZW compressed data ("..tostring(compressed:sub(1,1))..")"
        end
        compressed = compressed:sub(2)
        local dict_size = 256
        for k in pairs(dict) do
            dict[k] = nil
        end
        for i = 0, 255 do
            dict[i] = string_char(i)
        end
        local result = {}
        local t = 1
        local delta, k
        k, delta = decode(compressed,t)
        t = t + delta
        result[#result+1] = dict[k]
        local w = dict[k]
        local entry
        while t <= #compressed do
            k, delta = decode(compressed,t)
            t = t + delta
            entry = dict[k] or (w..w:sub(1,1))
            result[#result+1] = entry
            dict[dict_size] = w..entry:sub(1,1)
            dict_size = dict_size + 1
            w = entry
        end
        return table_concat(result)
    else
        return nil, "Can only uncompress strings"
    end
end


--------------------------------------------------------------------------------
-- Huffman codec
-- implemented by Galmok of European Stormrage (Horde), galmok@gmail.com

local function addCode(tree, bcode,len)
    if tree then
        tree.bcode = bcode;
        tree.blength = len;
        if tree.c1 then
            addCode(tree.c1, bit_bor(bcode, bit_lshift(1,len)), len+1)
        end
        if tree.c2 then
            addCode(tree.c2, bcode, len+1)
        end
    end
end

local function escape_code(code, len)
    local escaped_code = 0;
    local b;
    local l = 0;
    for i = len-1, 0,- 1 do
        b = bit_band( code, bit_lshift(1,i))==0 and 0 or 1
        escaped_code = bit_lshift(escaped_code,1+b) + b
        l = l + b;
    end
    return escaped_code, len+l
end

tables.Huffman_compressed = {}
tables.Huffman_large_compressed = {}

local compressed_size = 0
local remainder;
local remainder_length;
local function addBits(tbl, code, len)
    remainder = remainder + bit_lshift(code, remainder_length)
    remainder_length = len + remainder_length
    if remainder_length > 32 then
        return true -- Bits lost due to too long code-words.
    end
    while remainder_length>=8 do
        compressed_size = compressed_size + 1
        tbl[compressed_size] = string_char(bit_band(remainder, 255))
        remainder = bit_rshift(remainder, 8)
        remainder_length = remainder_length -8
    end
end

-- word size for this huffman algorithm is 8 bits (1 byte). This means the best compression is representing 1 byte with 1 bit, i.e. compress to 0.125 of original size.
function LibCompress:CompressHuffman(uncompressed)
    if not type(uncompressed)=="string" then
        return nil, "Can only compress strings"
    end
    if #uncompressed == 0 then
        return "\001"
    end
    
    -- make histogram
    local hist = {}
    local n = 0
    -- dont have to use all datat to make the histogram
    local uncompressed_size = string_len(uncompressed)
    local c;
    for i = 1, uncompressed_size do
        c = string_byte(uncompressed, i)
        hist[c] = (hist[c] or 0) + 1
    end

    --Start with as many leaves as there are symbols.
    local leafs = {}
    local leaf;
    local symbols = {}
    for symbol, weight in pairs(hist) do
        leaf = { symbol=string_char(symbol), weight=weight };
        symbols[symbol] = leaf;
        table_insert(leafs, leaf)
    end
    --Enqueue all leaf nodes into the first queue (by probability in increasing order so that the least likely item is in the head of the queue).
table.    sort(leafs, function(a,b) if a.weight<b.weight then return true elseif a.weight>b.weight then return false else return nil end end)

    local nLeafs = #leafs
    
    -- create tree
    local huff = {}
    --While there is more than one node in the queues:
    local l,h, li, hi, leaf1, leaf2
    local newNode;
    while (#leafs+#huff > 1) do
        -- Dequeue the two nodes with the lowest weight.
        -- Dequeue first
        if not next(huff) then
            li, leaf1 = next(leafs)
            table_remove(leafs, li)
        elseif not next(leafs) then
            hi, leaf1 = next(huff)
            table_remove(huff, hi)
        else
            li, l = next(leafs);
            hi, h = next(huff);
            if l.weight<=h.weight then
                leaf1 = l;
                table_remove(leafs, li)
            else
                leaf1 = h;
                table_remove(huff, hi)
            end
        end
        -- Dequeue second
        if not next(huff) then
            li, leaf2 = next(leafs)
            table_remove(leafs, li)
        elseif not next(leafs) then
            hi, leaf2 = next(huff)
            table_remove(huff, hi)
        else
            li, l = next(leafs);
            hi, h = next(huff);
            if l.weight<=h.weight then
                leaf2 = l;
                table_remove(leafs, li)
            else
                leaf2 = h;
                table_remove(huff, hi)
            end
        end

        --Create a new internal node, with the two just-removed nodes as children (either node can be either child) and the sum of their weights as the new weight.
        newNode = { c1 = leaf1, c2 = leaf2, weight = leaf1.weight+leaf2.weight }
        table_insert(huff,newNode)
    end
    if #leafs>0 then
        li, l = next(leafs)
        table_insert(huff, l)
        table_remove(leafs, li)
    end
    huff = huff[1];

    -- assign codes to each symbol
    -- c1 = "0", c2 = "1"
    -- As a common convention, bit '0' represents following the left child and bit '1' represents following the right child.
    -- c1 = left, c2 = right

    addCode(huff,0,0);
    if huff then
        huff.bcode = 0
        huff.blength = 1
    end
    
    -- READING
    -- bitfield = 0
    -- bitfield_len = 0
    -- read byte1
    -- bitfield = bitfield + bit_lshift(byte1, bitfield_len)
    -- bitfield_len = bitfield_len + 8
    -- read byte2
    -- bitfield = bitfield + bit_lshift(byte2, bitfield_len)
    -- bitfield_len = bitfield_len + 8
    -- (use 5 bits)
    --    word = bit_band( bitfield, bit_lshift(1,5)-1)
    --    bitfield = bit_rshift( bitfield, 5)
    --    bitfield_len = bitfield_len - 5
    -- read byte3
    -- bitfield = bitfield + bit_lshift(byte3, bitfield_len)
    -- bitfield_len = bitfield_len + 8
    
    -- WRITING
    remainder = 0;
    remainder_length = 0;
    
    local compressed = tables.Huffman_compressed
    --compressed_size = 0

    -- first byte is version info. 0 = uncompressed, 1 = 8-bit word huffman compressed
    compressed[1] = "\003"
    
    -- Header: byte 0=#leafs, byte 1-3=size of uncompressed data
    -- max 2^24 bytes
    local l = string_len(uncompressed)
    compressed[2] = string_char(bit_band(nLeafs-1, 255))            -- number of leafs
    compressed[3] = string_char(bit_band(l, 255))            -- bit 0-7
    compressed[4] = string_char(bit_band(bit_rshift(l, 8), 255))    -- bit 8-15
    compressed[5] = string_char(bit_band(bit_rshift(l, 16), 255))    -- bit 16-23
    compressed_size = 5
    
    -- create symbol/code map
    for symbol, leaf in pairs(symbols) do
        addBits(compressed, symbol, 8);
        if addBits(compressed, escape_code(leaf.bcode, leaf.blength)) then
            -- code word too long. Needs new revision to be able to handle more than 32 bits
            return string_char(0)..uncompressed
        end
        addBits(compressed, 3, 2);
    end

    -- create huffman code
    local large_compressed = tables.Huffman_large_compressed
    local large_compressed_size = 0
    local ulimit
    for i = 1, l, 200 do
        ulimit = l<(i+199) and l or (i+199)
        for sub_i = i, ulimit do
            c = string_byte(uncompressed, sub_i)
            addBits(compressed, symbols[c].bcode, symbols[c].blength)
        end
        large_compressed_size = large_compressed_size + 1
        large_compressed[large_compressed_size] = table_concat(compressed, "", 1, compressed_size)
        compressed_size = 0
    end
    
    -- add remainding bits (if any)
    if remainder_length>0 then
        large_compressed_size = large_compressed_size + 1
        large_compressed[large_compressed_size] = string_char(remainder)
    end
    local compressed_string = table_concat(large_compressed, "", 1, large_compressed_size)
    
    -- is compression worth it? If not, return uncompressed data.
    if (#uncompressed+1) <= #compressed_string then
        return "\001"..uncompressed
    end
    
    setCleanupTables("Huffman_compressed", "Huffman_large_compressed")
    return compressed_string
end

-- lookuptable (cached between calls)
local lshiftMask = {}
setmetatable(lshiftMask, {
    __index = function (t, k)
        local v = bit_lshift(1, k)
        rawset(t, k, v)
        return v
    end
})

-- lookuptable (cached between calls)
local lshiftMinusOneMask = {}
setmetatable(lshiftMinusOneMask, {
    __index = function (t, k)
        local v = bit_lshift(1, k)-1
        rawset(t, k, v)
        return v
    end
})

local function getCode(bitfield, field_len)
    if field_len>=2 then
        local b;
        local p = 0;
        for i = 0, field_len-1 do
            b = bit_band(bitfield, lshiftMask[i])
            if not (p==0) and not (b == 0) then
                -- found 2 bits set right after each other (stop bits)
                return bit_band( bitfield, lshiftMinusOneMask[i-1]), i-1, 
                    bit_rshift(bitfield, i+1), field_len-i-1
            end
            p = b
        end
    end
    return nil
end

local function unescape_code(code, code_len)
    local unescaped_code=0;
    local b;
    local l = 0;
    local i = 0
    while i < code_len do
        b = bit_band( code, lshiftMask[i])
        if not (b==0) then
            unescaped_code = bit_bor(unescaped_code, lshiftMask[l])
            i = i + 1
        end
        i = i + 1
        l = l + 1
    end
    return unescaped_code, l
end

tables.Huffman_uncompressed = {}
tables.Huffman_large_uncompressed = {} -- will always be as big as the larges string ever decompressed. Bad, but clearing i every timetakes precious time.

function LibCompress:DecompressHuffman(compressed)
    if not type(uncompressed)=="string" then
        return nil, "Can only uncompress strings"
    end

    local compressed_size = #compressed
    --decode header
    local info_byte = string_byte(compressed)
    -- is data compressed
    if info_byte==1 then
        return compressed:sub(2) --return uncompressed data
    end
    if not (info_byte==3) then
        return nil, "Can only decompress Huffman compressed data ("..tostring(info_byte)..")"
    end

    local num_symbols = string_byte(string_sub(compressed, 2, 2)) + 1
    local c0 = string_byte(string_sub(compressed, 3, 3))
    local c1 = string_byte(string_sub(compressed, 4, 4))
    local c2 = string_byte(string_sub(compressed, 5, 5))
    local orig_size = c2*65536 + c1*256 + c0
    if orig_size==0 then
        return "";
    end

    -- decode code->symbal map
    local bitfield = 0;
    local bitfield_len = 0;
    local map = {} -- only table not reused in Huffman decode.
    setmetatable(map, {
        __index = function (t, k)
            local v = {}
            rawset(t, k, v)
            return v
        end
    })
    
    local i = 6; -- byte 1-5 are header bytes
    local c, cl;
    local minCodeLen = 1000;
    local maxCodeLen = 0;
    local symbol, code, code_len, _bitfield, _bitfield_len;
    local n = 0;
    local state = 0; -- 0 = get symbol (8 bits),  1 = get code (varying bits, ends with 2 bits set)
    while n<num_symbols do
        if i>compressed_size then
            return nil, "Cannot decode map"
        end

        c = string_byte(compressed, i)
        bitfield = bit_bor(bitfield, bit_lshift(c, bitfield_len))
        bitfield_len = bitfield_len + 8
        
        if state == 0 then
            symbol = bit_band(bitfield, 255)
            bitfield = bit_rshift(bitfield, 8)
            bitfield_len = bitfield_len -8
            state = 1 -- search for code now
        else
            code, code_len, _bitfield, _bitfield_len = getCode(bitfield, bitfield_len)
            if code then
                bitfield, bitfield_len = _bitfield, _bitfield_len
                c, cl = unescape_code(code, code_len)
                map[cl][c]=string_char(symbol)
                minCodeLen = cl<minCodeLen and cl or minCodeLen
                maxCodeLen = cl>maxCodeLen and cl or maxCodeLen
                --print("symbol: "..string_char(symbol).."  code: "..tobinary(c, cl))
                n = n + 1
                state = 0 -- search for next symbol (if any)
            end
        end
        i=i+1
    end
    
    -- dont create new subtables for entries not in the map. Waste of space.
    -- But do return an empty table to prevent runtime errors. (instead of returning nil)
    local mt = {}
    setmetatable(map, {
        __index = function (t, k)
            return mt 
        end
    })
    
    local uncompressed = tables.Huffman_uncompressed
    local large_uncompressed = tables.Huffman_large_uncompressed
    local uncompressed_size = 0
    local large_uncompressed_size = 0
    local test_code
    local test_code_len = minCodeLen;
    local symbol;
    local dec_size = 0;
    compressed_size = compressed_size + 1
    local temp_limit = 200; -- first limit of uncompressed data. large_uncompressed will hold strings of length 200
    temp_limit = temp_limit > orig_size and orig_size or temp_limit
    while true do
        if test_code_len<=bitfield_len then 
            test_code=bit_band( bitfield, lshiftMinusOneMask[test_code_len])
            symbol = map[test_code_len][test_code]
            if symbol then
                uncompressed_size = uncompressed_size + 1
                uncompressed[uncompressed_size]=symbol
                dec_size = dec_size + 1
                if dec_size >= temp_limit then
                    if dec_size>=orig_size then -- checked here for speed reasons
                        break;
                    end
                    -- process compressed bytes in smaller chunks
                    large_uncompressed_size = large_uncompressed_size + 1
                    large_uncompressed[large_uncompressed_size] = table_concat(uncompressed, "", 1, uncompressed_size)
                    uncompressed_size = 0
                    temp_limit = temp_limit + 200 -- repeated chunk size is 200 uncompressed bytes
                    temp_limit = temp_limit > orig_size and orig_size or temp_limit
                end
                bitfield = bit_rshift(bitfield, test_code_len)
                bitfield_len = bitfield_len - test_code_len
                test_code_len = minCodeLen
            else
                test_code_len = test_code_len + 1
                if test_code_len>maxCodeLen then
                    return nil, "Decompression error at "..tostring(i).."/"..tostring(#compressed)
                end
            end
        else
            c = string_byte(compressed, i)
            bitfield = bitfield + bit_lshift(c or 0, bitfield_len)
            bitfield_len = bitfield_len + 8
            if i > compressed_size then
                break;
            end
            i = i + 1
        end
    end

    setCleanupTables("Huffman_uncompressed", "Huffman_large_uncompressed")
    return table_concat(large_uncompressed, "", 1, large_uncompressed_size)..table_concat(uncompressed, "", 1, uncompressed_size)
end

--------------------------------------------------------------------------------
-- Generic codec interface

function LibCompress:DecompressUncompressed(data)
    if type(data)~="string" then
        return nil, "Can only handle strings"
    end
    if string.byte(data) ~= 1 then
        return nil, "Can only handle uncompressed data"
    end
    return data:sub(2)
end

local compression_methods = {
    [2] = LibCompress.CompressLZW,
    [3] = LibCompress.CompressHuffman
}

local decompression_methods = {
    [1] = LibCompress.DecompressUncompressed,
    [2] = LibCompress.DecompressLZW,
    [3] = LibCompress.DecompressHuffman
}

-- try all compression codecs and return best result
function LibCompress:Compress(data)
    local method = next(compression_methods)
    local result = compression_methods[method](self, data);
    local n;
    method = next(compression_methods, method)
    while method do
        n = compression_methods[method](self, data)
        if #n < #result then
            result = n
        end
        method = next(compression_methods, method)
    end
    return result
end

function LibCompress:Decompress(data)
    local header_info = string.byte(data)
    if decompression_methods[header_info] then
        return decompression_methods[header_info](self, data)
    else
        return nil, "Unknown compression method ("..tostring(header_info)..")"
    end
end

----------------------------------------------------------------------
----------------------------------------------------------------------
--
-- Encoding algorithms

--------------------------------------------------------------------------------
-- Prefix encoding algorithm
-- implemented by Galmok of European Stormrage (Horde), galmok@gmail.com

--[[
    Howto: Encode and Decode:

    3 functions are supplied, 2 of them are variants of the first.  They return a table with functions to encode and decode text.
    
    table, msg = LibCompress:GetEncodeTable(reservedChars, escapeChars,  mapChars)
    
        reservedChars: The characters in this string will not appear in the encoded data.
        escapeChars: A string of characters used as escape-characters (don't supply more than needed). #escapeChars >= 1
        mapChars: First characters in reservedChars maps to first characters in mapChars.  (#mapChars <= #reservedChars)
    
    return value:
        table
            if nil then msg holds an error message, otherwise use like this:
    
            encoded_message = table:Encode(message)
            message = table:Decode(encoded_message)
            
    GetAddonEncodeTable: Sets up encoding for the addon channel (\000 is encoded)
    GetChatEncodeTable: Sets up encoding for the chat channel (many bytes encoded, see the function for details)
    
    Except for the mapped characters, all encoding will be with 1 escape character followed by 1 suffix, i.e. 2 bytes.
]]
-- to be able to match any requested byte value, the search string must be preprocessed
-- characters to escape with %:
-- ( ) . % + - * ? [ ] ^ $
-- "illegal" byte values: 
-- 0 is replaces %z
local gsub_escape_table = {
    ['\000'] = "%z",
    [('(')] = "%(",
    [(')')] = "%)",
    [('.')] = "%.",
    [('%')] = "%%",
    [('+')] = "%+",
    [('-')] = "%-",
    [('*')] = "%*",
    [('?')] = "%?",
    [('[')] = "%[",
    [(']')] = "%]",
    [('^')] = "%^",
    [('$')] = "%$"
}

local function escape_for_gsub(str)
    return str:gsub("([%z%(%)%.%%%+%-%*%?%[%]%^%$])",  gsub_escape_table)
end

function LibCompress:GetEncodeTable(reservedChars, escapeChars, mapChars)
    reservedChars = reservedChars or ""
    escapeChars = escapeChars or ""
    mapChars = mapChars or ""
    
    -- select a default escape character
    if escapeChars == "" then
        return nil, "No escape characters supplied"
    end
    
    if #reservedChars < #mapChars then
        return nil, "Number of reserved characters must be at least as many as the number of mapped chars"
    end
    
    if reservedChars == "" then
        return nil, "No characters to encode"
    end
    
    -- list of characters that must be encoded
    encodeBytes = reservedChars..escapeChars..mapChars
    
    -- build list of bytes not available as a suffix to a prefix byte
    local taken = {}
    for i=1, strlen(encodeBytes) do 
        taken[string.sub(encodeBytes, i, i)] = true
    end
    
    -- allocate a table to holde encode/decode strings/functions
    local codecTable = {}
    
    -- the encoding can be a single gsub, but the decoding can require multiple gsubs
    local decode_func_string = {}
    
    local encode_search = {}
    local encode_translate = {}
    local decode_search = {}
    local decode_translate = {}
    local c,r,i,to,from
    local escapeCharIndex = 0
    
    -- map single byte to single byte
    if #mapChars > 0 then
        for i=1, #mapChars do
            from = string.sub(reservedChars, i, i)
            to = string.sub(mapChars, i, i)
            encode_translate[from] = to
            table.insert(encode_search, from)
            decode_translate[to] = from
            table.insert(decode_search, to)
        end
        codecTable["decode_search"..tostring(escapeCharIndex)] = "([".. escape_for_gsub(table.concat(decode_search)).."])"
        codecTable["decode_translate"..tostring(escapeCharIndex)] = decode_translate
        tinsert(decode_func_string, "str = str:gsub(self.decode_search"..tostring(escapeCharIndex)..", self.decode_translate"..tostring(escapeCharIndex)..");")

    end
    
    -- map single byte to double-byte
    escapeCharIndex = escapeCharIndex +1
    escapeChar = string.sub(escapeChars, escapeCharIndex, escapeCharIndex)
    r = 0 -- suffix char value to the escapeChar
    decode_search = {}
    decode_translate = {}
    for i = 1, strlen(encodeBytes) do
        c = string.sub(encodeBytes, i, i)
        if not encode_translate[c] then
            -- this loop will update escapeChar and r
            while r<256 and taken[string.char(r)] do
                r=r+1
                if r>255 then -- switch to next escapeChar
                    if escapeChar == "" then -- we are out of escape chars and we need more!
                        return nil, "Out of escape characters"
                    end
                    
                    codecTable["decode_search"..tostring(escapeCharIndex)] = escape_for_gsub(escapeChar).."([".. escape_for_gsub(table.concat(decode_search)).."])"
                    codecTable["decode_translate"..tostring(escapeCharIndex)] = decode_translate
                    tinsert(decode_func_string, "str = str:gsub(self.decode_search"..tostring(escapeCharIndex)..", self.decode_translate"..tostring(escapeCharIndex)..");")
                    
                    escapeCharIndex  = escapeCharIndex + 1
                    escapeChar = string.sub(escapeChars, escapeCharIndex, escapeCharIndex)
                    
                    r = 0
                    decode_search = {}
                    decode_translate = {}
                end
            end
            encode_translate[c] = escapeChar..string.char(r)
            table.insert(encode_search, c)
            decode_translate[string.char(r)] = c
            table.insert(decode_search, string.char(r))
            r = r + 1
        end
    end
    if r>0 then
        codecTable["decode_search"..tostring(escapeCharIndex)] = escape_for_gsub(escapeChar).."([".. escape_for_gsub(table.concat(decode_search)).."])"
        codecTable["decode_translate"..tostring(escapeCharIndex)] = decode_translate
        tinsert(decode_func_string, "str = str:gsub(self.decode_search"..tostring(escapeCharIndex)..", self.decode_translate"..tostring(escapeCharIndex)..");")
    end
    
    -- change last line from "str = ...;" to "return ...;";
    decode_func_string[#decode_func_string] = decode_func_string[#decode_func_string]:gsub("str = (.*);", "return %1;");
    decode_func_string = "return function(self, str) "..table.concat(decode_func_string).." end"
    
    encode_search = "([".. escape_for_gsub(table.concat(encode_search)).."])"
    decode_search = escape_for_gsub(escapeChars).."([".. escape_for_gsub(table.concat(decode_search)).."])"
    
    encode_func = assert(loadstring("return function(self, str) return str:gsub(self.encode_search, self.encode_translate); end"))()
    decode_func = assert(loadstring(decode_func_string))()
    
    codecTable.encode_search = encode_search
    codecTable.encode_translate = encode_translate
    codecTable.Encode = encode_func
    codecTable.decode_search = decode_search
    codecTable.decode_translate = decode_translate
    codecTable.Decode = decode_func

    codecTable.decode_func_string = decode_func_string -- to be deleted
    return codecTable
end

-- Addons: Call this only once and reuse the returned table for all encodings/decodings. 
function LibCompress:GetAddonEncodeTable(reservedChars, escapeChars, mapChars )
    reservedChars = reservedChars or ""
    escapeChars = escapeChars or ""
    mapChars = mapChars or ""
    -- Following byte values are not allowed:
    -- \000
    if escapeChars == "" then
        escapeChars = "\001"
    end
    return self:GetEncodeTable( (reservedChars or "").."\000", escapeChars, mapChars)
end

-- Addons: Call this only once and reuse the returned table for all encodings/decodings.
function LibCompress:GetChatEncodeTable(reservedChars, escapeChars, mapChars)
    reservedChars = reservedChars or ""
    escapeChars = escapeChars or ""
    mapChars = mapChars or ""
    -- Following byte values are not allowed:
    -- \000, s, S, \010, \013, \124, %
    -- Because SendChatMessage will error if an UTF8 multibyte character is incomplete,
    -- all character values above 127 have to be encoded to avoid this. This costs quite a bit of bandwidth (about 13-14%)
    -- Also, because drunken status is unknown for the received, strings used with SendChatMessage should be terminated with
    -- an identifying byte value, after which the server MAY add "...hic!" or as much as it can fit(!). 
    -- Pass the identifying byte as a reserved character to this function to ensure the encoding doesn't contain that value.
    --  or use this: local message, match = arg1:gsub("^(.*)\029.-$", "%1")
    --  arg1 is message from channel, \029 is the string terminator, but may be used in the encoded datastream as well. :-)
    -- This encoding will expand data anywhere from:
    -- 0% (average with pure ascii text)
    -- 53.5% (average with random data valued zero to 255) 
    -- 100% (only encoding data that encodes to two bytes)
    local i
    local r={}
    for i=128, 255 do
        table.insert(r, string.char(i))
    end
    reservedChars = "sS\000\010\013\124%"..table.concat(r)..(reservedChars or "")
    if escapeChars == "" then
        escapeChars = "\029\031"
    end
    if mapChars == "" then
        mapChars = "\015\020";
    end
    return self:GetEncodeTable(reservedChars, escapeChars, mapChars)
end

--------------------------------------------------------------------------------
-- 7 bit encoding algorithm
-- implemented by Galmok of European Stormrage (Horde), galmok@gmail.com

-- The encoded data holds values from 0 to 127 inclusive. Additional encoding may be necessary.
-- This algorithm isn't exactly fast and be used with care and consideration

tables.encode7bit = {}

function LibCompress:Encode7bit(str)
    local remainder = 0;
    local remainder_length = 0;
    local tbl = tables.encode7bit
    local encoded_size = 0
    local l=#str
    for i=1,l do
        code = string.byte(str, i)
        remainder = remainder + bit_lshift(code, remainder_length)
        remainder_length = 8 + remainder_length
        while remainder_length>=7 do
            encoded_size = encoded_size + 1
            tbl[encoded_size] = string_char(bit_band(remainder, 127))
            remainder = bit_rshift(remainder, 7)
            remainder_length = remainder_length -7
        end
    end
    if remainder_length>0 then
        encoded_size = encoded_size + 1
        tbl[encoded_size] = string_char(remainder)
    end
    setCleanupTables("encode7bit")
    return table.concat(tbl, "", 1, encoded_size)
end

tables.decode8bit = {}

function LibCompress:Decode7bit(str)
    local bit8 = tables.decode8bit
    local decoded_size = 0
    local ch
    local i=1
    local bitfield_len=0
    local bitfield=0
    local l=#str
    while true do
        if bitfield_len >=8 then
            decoded_size = decoded_size + 1
            bit8[decoded_size] = string_char(bit.band(bitfield, 255))
            bitfield = bit_rshift(bitfield, 8)
            bitfield_len = bitfield_len - 8
        end
        ch=string_byte(str,i)
        bitfield=bitfield+bit_lshift(ch or 0, bitfield_len)
        bitfield_len = bitfield_len + 7
        if i > l then
            break
        end
        i=i+1
    end
    setCleanupTables("decode8bit")
    return table.concat(bit8, "", 1, decoded_size)
end

----------------------------------------------------------------------
----------------------------------------------------------------------
--
-- Checksum/hash algorithms

--------------------------------------------------------------------------------
-- FCS16/32 checksum algorithms
-- converted from C by Galmok of European Stormrage (Horde), galmok@gmail.com
-- usage:
--     code = LibCompress:fcs16init()
--    code = LibCompress:fcs16update(code, data1)
--    code = LibCompress:fcs16update(code, data2)
--    code = LibCompress:fcs16update(code, data...)
--    code = LibCompress:fcs16final(code)
--
--    data = string
--    fcs16 provides a 16 bit checksum, fcs32 provides a 32 bit checksum.


--[[/* The following copyright notice concerns only the FCS hash algorithm
---------------------------------------------------------------------------
Copyright (c) 2003, Dominik Reichl <dominik.reichl@t-online.de>, Germany.
All rights reserved.

Distributed under the terms of the GNU General Public License v2.

This software is provided 'as is' with no explicit or implied warranties
in respect of its properties, including, but not limited to, correctness
and/or fitness for purpose.
---------------------------------------------------------------------------
*/]]
--// FCS-16 algorithm implemented as described in RFC 1331
local FCSINIT16 = 65535;
--// Fast 16 bit FCS lookup table
local fcs16tab = { [0]=0, 4489, 8978, 12955, 17956, 22445, 25910, 29887, 
    35912, 40385, 44890, 48851, 51820, 56293, 59774, 63735,
    4225, 264, 13203, 8730, 22181, 18220, 30135, 25662,
    40137, 36160, 49115, 44626, 56045, 52068, 63999, 59510,
    8450, 12427, 528, 5017, 26406, 30383, 17460, 21949,
    44362, 48323, 36440, 40913, 60270, 64231, 51324, 55797,
    12675, 8202, 4753, 792, 30631, 26158, 21685, 17724,
    48587, 44098, 40665, 36688, 64495, 60006, 55549, 51572,
    16900, 21389, 24854, 28831, 1056, 5545, 10034, 14011,
    52812, 57285, 60766, 64727, 34920, 39393, 43898, 47859,
    21125, 17164, 29079, 24606, 5281, 1320, 14259, 9786,
    57037, 53060, 64991, 60502, 39145, 35168, 48123, 43634,
    25350, 29327, 16404, 20893, 9506, 13483, 1584, 6073,
    61262, 65223, 52316, 56789, 43370, 47331, 35448, 39921,
    29575, 25102, 20629, 16668, 13731, 9258, 5809, 1848,
    65487, 60998, 56541, 52564, 47595, 43106, 39673, 35696,
    33800, 38273, 42778, 46739, 49708, 54181, 57662, 61623,
    2112, 6601, 11090, 15067, 20068, 24557, 28022, 31999,
    38025, 34048, 47003, 42514, 53933, 49956, 61887, 57398,
    6337, 2376, 15315, 10842, 24293, 20332, 32247, 27774,
    42250, 46211, 34328, 38801, 58158, 62119, 49212, 53685,
    10562, 14539, 2640, 7129, 28518, 32495, 19572, 24061,
    46475, 41986, 38553, 34576, 62383, 57894, 53437, 49460,
    14787, 10314, 6865, 2904, 32743, 28270, 23797, 19836,
    50700, 55173, 58654, 62615, 32808, 37281, 41786, 45747,
    19012, 23501, 26966, 30943, 3168, 7657, 12146, 16123,
    54925, 50948, 62879, 58390, 37033, 33056, 46011, 41522,
    23237, 19276, 31191, 26718, 7393, 3432, 16371, 11898,
    59150, 63111, 50204, 54677, 41258, 45219, 33336, 37809,
    27462, 31439, 18516, 23005, 11618, 15595, 3696, 8185,
    63375, 58886, 54429, 50452, 45483, 40994, 37561, 33584,
    31687, 27214, 22741, 18780, 15843, 11370, 7921, 3960 }

function LibCompress:fcs16init()
    return FCSINIT16;
end

function LibCompress:fcs16update(uFcs16, pBuffer)
    local i
    local l=string_len(pBuffer)
    for i = 1,l do
        uFcs16 = bit_bxor(bit_rshift(uFcs16,8), fcs16tab[bit_band(bit_bxor(uFcs16, string_byte(pBuffer,i)), 255)])
    end
    return uFcs16
end

function LibCompress:fcs16final(uFcs16)
    return bit_bxor(uFcs16,65535)
end
-- END OF FCS16

--[[/*
---------------------------------------------------------------------------
Copyright (c) 2003, Dominik Reichl <dominik.reichl@t-online.de>, Germany.
All rights reserved.

Distributed under the terms of the GNU General Public License v2.

This software is provided 'as is' with no explicit or implied warranties
in respect of its properties, including, but not limited to, correctness
and/or fitness for purpose.
---------------------------------------------------------------------------
*/]]

--// FCS-32 algorithm implemented as described in RFC 1331

local FCSINIT32 = -1

--// Fast 32 bit FCS lookup table
local fcs32tab = {  [0]=0, 1996959894, -301047508, -1727442502, 124634137, 1886057615, -379345611, -1637575261,
    249268274, 2044508324, -522852066, -1747789432, 162941995, 2125561021, -407360249, -1866523247,
    498536548, 1789927666, -205950648, -2067906082, 450548861, 1843258603, -187386543, -2083289657,
    325883990, 1684777152, -43845254, -1973040660, 335633487, 1661365465, -99664541, -1928851979,
    997073096, 1281953886, -715111964, -1570279054, 1006888145, 1258607687, -770865667, -1526024853,
    901097722, 1119000684, -608450090, -1396901568, 853044451, 1172266101, -589951537, -1412350631,
    651767980, 1373503546, -925412992, -1076862698, 565507253, 1454621731, -809855591, -1195530993,
    671266974, 1594198024, -972236366, -1324619484, 795835527, 1483230225, -1050600021, -1234817731,
    1994146192, 31158534, -1731059524, -271249366, 1907459465, 112637215, -1614814043, -390540237,
    2013776290, 251722036, -1777751922, -519137256, 2137656763, 141376813, -1855689577, -429695999,
    1802195444, 476864866, -2056965928, -228458418, 1812370925, 453092731, -2113342271, -183516073,
    1706088902, 314042704, -1950435094, -54949764, 1658658271, 366619977, -1932296973, -69972891,
    1303535960, 984961486, -1547960204, -725929758, 1256170817, 1037604311, -1529756563, -740887301,
    1131014506, 879679996, -1385723834, -631195440, 1141124467, 855842277, -1442165665, -586318647,
    1342533948, 654459306, -1106571248, -921952122, 1466479909, 544179635, -1184443383, -832445281,
    1591671054, 702138776, -1328506846, -942167884, 1504918807, 783551873, -1212326853, -1061524307,
    -306674912, -1698712650, 62317068, 1957810842, -355121351, -1647151185, 81470997, 1943803523,
    -480048366, -1805370492, 225274430, 2053790376, -468791541, -1828061283, 167816743, 2097651377,
    -267414716, -2029476910, 503444072, 1762050814, -144550051, -2140837941, 426522225, 1852507879,
    -19653770, -1982649376, 282753626, 1742555852, -105259153, -1900089351, 397917763, 1622183637,
    -690576408, -1580100738, 953729732, 1340076626, -776247311, -1497606297, 1068828381, 1219638859,
    -670225446, -1358292148, 906185462, 1090812512, -547295293, -1469587627, 829329135, 1181335161,
    -882789492, -1134132454, 628085408, 1382605366, -871598187, -1156888829, 570562233, 1426400815,
    -977650754, -1296233688, 733239954, 1555261956, -1026031705, -1244606671, 752459403, 1541320221,
    -1687895376, -328994266, 1969922972, 40735498, -1677130071, -351390145, 1913087877, 83908371,
    -1782625662, -491226604, 2075208622, 213261112, -1831694693, -438977011, 2094854071, 198958881,
    -2032938284, -237706686, 1759359992, 534414190, -2118248755, -155638181, 1873836001, 414664567,
    -2012718362, -15766928, 1711684554, 285281116, -1889165569, -127750551, 1634467795, 376229701,
    -1609899400, -686959890, 1308918612, 956543938, -1486412191, -799009033, 1231636301, 1047427035,
    -1362007478, -640263460, 1088359270, 936918000, -1447252397, -558129467, 1202900863, 817233897,
    -1111625188, -893730166, 1404277552, 615818150, -1160759803, -841546093, 1423857449, 601450431,
    -1285129682, -1000256840, 1567103746, 711928724, -1274298825, -1022587231, 1510334235, 755167117 }

function LibCompress:fcs32init()
    return FCSINIT32;
end

function LibCompress:fcs32update(uFcs32, pBuffer)
    local i
    local l=string_len(pBuffer)
    for i = 1,l do
        uFcs32 = bit_bxor(bit_rshift(uFcs32,8), fcs32tab[bit_band(bit_bxor(uFcs32, string_byte(pBuffer,i)), 255)])
    end
    return uFcs32
end

function LibCompress:fcs32final(uFcs32)
    return bit_bnot(uFcs32)
end

return LibCompress

end

zip = preload_1016()


--*************************************************************************
--  LIBRARIES_END
--*************************************************************************
-- MAIN_SECTION_BEGIN
--*************************************************************************



FA_OPEN_EXISTING = 0x00
FA_READ          = 0x01 
FA_WRITE         = 0x02 
FA_CREATE_NEW    = 0x04 
FA_CREATE_ALWAYS = 0x08 
FA_OPEN_ALWAYS   = 0x10 
ladebalken       = 0
sd_dir           = '---'
sd_subdirs       = {}
all_files        = {}
add_write        = FA_WRITE|FA_READ
over_write       = FA_CREATE_ALWAYS|FA_WRITE
WriteOnceSize    = 2048
sdcard_debug_txt = ''
lang_nr          = 0   
transl           = {}
t_cache          = {}

--*****************************************************************************
--core


function on_init ()
--    uart_setup(0,9600,8,1,1)
    uart_set_timeout(20,10)

    if file_open == nil then
        print("PATCH")
        local patch_file = "patch_"..string.format("%07u",tonumber(SERIALNUMBER))..".enc"
        local file_handle = io.open(patch_file)
        if file_handle ~= nil then
            file_handle:close()
            load_patch(patch_file)
        end
    end
    
    app = App:new(0)
    app.debug = 0

end

uart_free_protocol = 1

--**********************************************************************

function on_control_notify (sid,control_nr,val)
    return app:on_control_notify(sid,control_nr,val)
end

--**********************************************************************

function on_screen_change (sid)
    if app ~= nil then
--        print('ID',sid) 
        app:on_screen_change(sid)
    end
end

--**********************************************************************

function on_uart_recv_data(packet)
    app:on_uart_recv_data(packet,"comes from real hardware")
end

--**********************************************************************

function on_timer (tid)
    if tid == 11 then
        load_patch(PATCH_filename)   --  einlesen der einzelnen parts des patchfiles
    elseif tid == 12 then
        load_parts()                 --  zusammenfuehren der einezlen parts und ausfuehren, so dass alle patch funktionen ausgefuehrt werden
    else
        return app:on_timer_pre(tid)
    end
end

--**********************************************************************

function encode_general (text,alpha,alphb,nra,nrb)  --  encode bit series to xx chars string
    
    text = pad_up(text,string.sub(alpha,1,1),nra)

    encoded_text = {}
    
    while 0 == 0 do
    
        local chunk_text = string.sub(text,1,nra)
        text             = string.sub(text,nra+1)
        local chunk_nr   = 0
        
        for i=nra,1,-1 do
            local char_aktuell = string.sub(chunk_text,i,i)
--            print("XXX",alpha,chunk_nr,char_aktuell)
--            if char_aktuell == ":" then
--                print(text)
--            end
            chunk_nr           = chunk_nr * #alpha + string.find(alpha,char_aktuell) - 1
        end

        for i=nrb,1,-1 do
            local pos      = math.floor(chunk_nr % #alphb) + 1
            local new_char = string.sub(alphb,pos,pos)
            table.insert(encoded_text,new_char)
            chunk_nr  = (chunk_nr + 0.001) // #alphb + 0.001
        end

        if #text == 0 then
            break
        end

    end

    text = pad_down( table.concat(encoded_text), string.sub(alphb,1,1) )
    
    return(text)


end

--**********************************************************************

function mask_general (text,mask_char,mask_table)

    local text2   = {}
    local zaehler = 0
    
    while 0 == 0 do
        zaehler            = zaehler + 1
        local char_aktuell = string.sub(text,zaehler,zaehler)
        if char_aktuell == "" then
            break
        end
        local char_alt     = mask_table[char_aktuell]
        if char_alt ~= nil then
            table.insert(text2,mask_char..char_alt)
        else
            table.insert(text2,char_aktuell)
        end
    end
    
    return(table.concat(text2))
    
end

--**********************************************************************

function demask_general (text,mask_char,mask_table)

    local text2   = {}
    local zaehler = 0
    
    while 0 == 0 do
        zaehler            = zaehler + 1
        local char_aktuell = string.sub(text,zaehler,zaehler)
        if char_aktuell == "" then
            break
        end
        if char_aktuell == mask_char then
            zaehler      = zaehler + 1
            char_aktuell = mask_table[string.sub(text,zaehler,zaehler)]
        end
        table.insert(text2,char_aktuell)
--        print("---",text2)
    end
    
    return(table.concat(text2))
    
end

--**********************************************************************

function pad_up (text,pad_char,pad_nr) 

    while #text % pad_nr > 0 do  --  padding of entry text
        text = text .. pad_char
    end
    return(text)

end

--**********************************************************************

function pad_down (text,pad_char)

    while 0 == 0 do   -- de-padding
        if string.sub(text,#text,#text) == pad_char then
            text = string.sub(text,1,#text-1)
        else
            break
        end
    end
    
    return( text )
    
end 


--**********************************************************************

function base27_encode (text)

    local text1_crypt_bin = bin.stohex(text)

--    text1_crypt_bin = text1_crypt_bin .. "ff"

    text1_crypt_bin = table.concat(bin.hextobin(text1_crypt_bin))

    return( encode_general(text1_crypt_bin,CHAR02,CHAR27,19,4) )
    
end   

--**********************************************************************

function base27_decode (text)

    local text2 = encode_general(text1,CHAR27,CHAR02,4,19)
    local text3 = {}
    
    zaehler = 0
    while 0 == 0 do
        zaehler = zaehler + 1
        if string.sub(text2,zaehler,zaehler) == "" then
            break
        end
        table.insert(text3,tonumber(string.sub(text2,zaehler,zaehler)))
    end
    
    local text4 = bin.bintohex(text3)
    local text5 = bin.hextos(text4)

    return( text5 )
    
end   

--**********************************************************************

function base62_encode (text)

    local text1_crypt_bin = bin.stohex(text)

    text1_crypt_bin = text1_crypt_bin .. "ff"

    text1_crypt_bin = table.concat(bin.hextobin(text1_crypt_bin))
    
    local text1 = encode_general(text1_crypt_bin,CHAR02,CHAR64,6,1)
    local text2 = mask_general(text1,"0",MASK62FORWARD)
    return(text2)
    
end   

--**********************************************************************

function base62_decode (text)

    local text1 = demask_general(text,"0",MASK62REVERSE)
    local text2 = pad_up( encode_general(text1,CHAR64,CHAR02,1,6), "0",8 )
    local text3 = {}
    
    zaehler = 0
    while 0 == 0 do
        zaehler = zaehler + 1
        if string.sub(text2,zaehler,zaehler) == "" then
            break
        end
        table.insert(text3,tonumber(string.sub(text2,zaehler,zaehler)))
    end
    
    local text4 = bin.bintohex(text3)
    text4 = string.sub(text4,1,#text4-2)
    local text5 = bin.hextos(text4)
    
    return(text5)
    
end   

--**********************************************************************

function base32_encode (text)

    local text1_crypt_bin = bin.stohex(text)

    text1_crypt_bin = text1_crypt_bin .. "ff"

    text1_crypt_bin = table.concat(bin.hextobin(text1_crypt_bin))
    
    local text1 = encode_general(text1_crypt_bin,CHAR02,CHAR32,5,1)
    return(text1)
    
end   

--**********************************************************************

function base32_decode (text)

    local text2 = pad_up( encode_general(text,CHAR32,CHAR02,1,5), "0",8 )
    local text3 = {}
    
    zaehler = 0
    while 0 == 0 do
        zaehler = zaehler + 1
        if string.sub(text2,zaehler,zaehler) == "" then
            break
        end
        table.insert(text3,tonumber(string.sub(text2,zaehler,zaehler)))
    end
    
    local text4 = bin.bintohex(text3)
    text4 = string.sub(text4,1,#text4-2)
    local text5 = bin.hextos(text4)
    
    return(text5)
    
end   

--**********************************************************************

function t (expression,nr)

    if nr == nil then
        nr = lang_nr
    end
    
--    local erg = t_cache[tonumber(nr) .. expression]
--    if erg ~= nil then
--        return erg
--    end

--    print("EXPR",expression)


    if expression == "Parkzeit" then
        if PRUEFCODE % 10 > 0 then
            expression = "Anzahl Tickets"
        end
    end


    local x     = string.find(translation_table,"\n[^\n]-;"..expression..";")
    if x == nil then
        if translation_table == "" then
            return("")
        else
            return expression
        end
    end
    local ttab1 = string.sub(translation_table,x+1)
    x           = string.find(ttab1,"\n")
    local ttab2 = string.sub(ttab1,1,x-1)
    x = ";.-"
    local ttab3 = string.match(ttab2,x:rep(nr)..";(.-);")
    if ttab3 == nil or ttab3 == "-" then
        erg = ""
    else        
        erg = ttab3
    end

--     t_cache[tonumber(nr) .. expression] = erg
     return(erg)

end


--*************************************************************************

function text_to_numbers (text)

    local number_text = ""

    zaehler = 0
    while (0 == 0) do
        zaehler = zaehler + 1
        sign = string.sub(text,zaehler,zaehler)
        if sign == "" then
            break
        end
        number_text = number_text .. "," .. tostring(string.byte(sign))
    end

    return(number_text)

end

--*************************************************************************

function numbers_to_text (numbers)

    local text = ""

    for zeile in string.gmatch(numbers,'[^,]+') do
        text = text .. string.char(tonumber(zeile))
    end
    return(text)

end

--**********************************************************************

function replace_diacritic_letters (text,dia_mode)
          
    local text1 = text_to_numbers(text)

    if dia_mode == "DE" then
        for key,val in pairs(DIACRITIC_REPLACE_DE) do
            if (dia_mode == nil) then
                break
            end
            local key1 = text_to_numbers(key)
            local val1 = text_to_numbers(val)
            text1      = string.gsub(text1,key1,val1)
        end
    end     
 
    for key,val in pairs(DIACRITIC_REPLACE) do
        local key1 = text_to_numbers(key)
        local val1 = text_to_numbers(val)
        text1      = string.gsub(text1,key1,val1)
    end     

    text = numbers_to_text(text1)
    return(text)

end

--**********************************************************************

function my_debug_print(debugInfo)
    --print(debugInfo)
    --sdcard_debug_txt=sdcard_debug_txt..debugInfo
    --debugInfo=get_text(2,8)..debugInfo
    --set_text(2,8,debugInfo)
    --set_text(3,8,debugInfo)
--    if open_debug_uatr == 1
--    then
--        print(debugInfo)
--    end
end


--**********************************************************************


--Write data to the end of the file
--@file:file path
--@data:The type of '@data' only be [string] or [byte array]
--@open_mode:open file mode



--**********************************************************************

function my_getdataLen_Type(data)
    
    my_debug_print('---------- my_getdataLen_Type ----------')

    local datalen = -1
    --获取数据类型
    local data_type = type(data)
    
    --计算数据长度
    if data_type == 'string'
    then
        datalen = string.len(data)
        
    elseif data_type == 'table'
    then
        datalen = #(data)
    end
    
    my_debug_print('Lua_debug data Type and Len: '..data_type..' / '..datalen)
    
    return datalen,data_type
end


--**********************************************************************

function my_write_filedata1(file0, file, data, open_mode)
    
    local erg = ""
    if file0 == nil or file_open ~= nil then
        erg = my_write_filedata(file,data,open_mode)
    else
        f = io.open(file0,'w')
        f:write(data)
        f:close()
    end

end

--**********************************************************************
--core

function my_read_filedata1(file0, file)
    
    if file0 == nil or file_open ~= nil then
        return( my_read_filedata(file,data,open_mode) )
    else
        local file1 = io.open(file0)
        local file2 = ""
        while 0 == 0 do
            if file1 == nil then
                break
            end
            local line1 = file1:read()
            if line1 == nil then
                file1:close()
                break
            end
            file2 = file2 .. line1 .. "\n"
        end
        return(file2)
    end

end

--**********************************************************************

function my_write_filedata(file, data, open_mode)
    
    my_debug_print('---------- my_write_filedata ----------')
    my_debug_print('Lua_debug: file -> '..file..' / data -> '..type(data)..' / open_mode -> '..open_mode)
    --local sc_prompt=1    
    local count     = 0
    local write_cnt = 0
    local seek_ops  = 0
    local all_byte  = 0
    
    --获取待写入数据的数据类型和长度
    local wrire_len, data_type = my_getdataLen_Type(data)
    
    local write_byte_Tb = {}
    local open_state = file_open(file, open_mode)
    
    local erg = ""
    
    if open_state == true
    then
        --获取当前文件大小,仅在追加文件末尾写入执行
        if open_mode == add_write
        then
            all_byte = file_size()
        end
        
        if wrire_len > 0
        then
            --计算'@data'要写多少次
            write_cnt =  math.modf(wrire_len / WriteOnceSize)
            if wrire_len % WriteOnceSize > 0
            then
                write_cnt = write_cnt + 1
            end
            my_debug_print('Lua_debug: need write allcnt -> '..write_cnt)
            
            for i = 1, write_cnt
            do
                --复位写字节数组
                write_byte_Tb = {}
                
                --计算写的位置
                seek_ops = (i - 1) * WriteOnceSize +all_byte
                file_seek(seek_ops)
                --文件偏移失败
                if offst_result == false 
                then
                    print('When writing the file, an offset error occurred. please try again! ! !')
                    break
                end
                my_debug_print('Lua_debug: cur seek_ops  -> '..seek_ops)
                
                --计算本次写的个数
                count = WriteOnceSize
                if i == write_cnt
                then
                    if wrire_len % WriteOnceSize > 0
                    then
                        count = wrire_len % WriteOnceSize
                    end
                end
                my_debug_print('Lua_debug: cur write  -> '..write_cnt..'th / wrire count '..count)
                
                --填充写入flash的字节数组
                for j = 1, count 
                do
                    if data_type == 'string'
                    then
                        --字符串类型,将每个字符转换为字节数组
                        write_byte_Tb[j - 1] = tonumber(string.byte(data, ((i - 1) * WriteOnceSize + j), ((i - 1) * WriteOnceSize + j)))
                        
                    elseif data_type == 'table'
                    then
                        --数组类型,字节赋值
                        write_byte_Tb[j - 1] = data[((i - 1) * WriteOnceSize + j)]
                    end
                end
                
                local IswriteOK = file_write(write_byte_Tb)
                if IswriteOK == false
                then
                    i = i - 1
                end    
            end
        end
        
    else
        print('The file does not exist, please check the contents of the SD card! ! !')
        erg = "ERROR"
    end
    
    
    --关闭文件
    file_close()
    return(erg)
end

--**********************************************************************
--core

function my_read_filedata(file)
    my_debug_print('---------- my_read_filedata ----------')
    my_debug_print('Lua_debug: file -> '..file)

    local count    = 0
    local read_cnt = 0
    local offset    = 0
    local all_byte = 0

    local read_byte_Tb = {}
    local read_char_Tb = {}

    local read_str     = ''
    local open_state = file_open(file, FA_READ)
    if open_state == true then
        --获取当前文件大小
        all_byte = file_size()
        if all_byte > 0 then
            read_cnt =  math.modf(all_byte/WriteOnceSize)
            if all_byte % WriteOnceSize > 0
            then
                read_cnt = read_cnt + 1
            end
            my_debug_print('Lua_debug: need read allcnt -> '..read_cnt)
            
            for i = 1, read_cnt
            do
                --复位读字节数组
                read_byte_Tb = {}
                read_char_Tb = {}
                
                --计算读取的偏移位置
                offset = (i - 1) * WriteOnceSize
                local offst_result = file_seek(offset)
                --文件偏移失败
                if offst_result == false 
                then
                    print('When reading the file, an offset error occurred. please try again! ! !')
                    
                    break
                end
                my_debug_print('Lua_debug: cur offset  -> '.. offset)

                --计算本次读的个数
                count = WriteOnceSize
                if i == read_cnt
                then
                    if all_byte % WriteOnceSize > 0
                    then
                        count = all_byte % WriteOnceSize
                    end
                end
                my_debug_print('Lua_debug: cur read  -> '..i..'th / wrire count '..count)
                
                --读取字节转换为字符并拼接成字符串
                read_byte_Tb = file_read(count)
                if #(read_byte_Tb) > 0
                then
                    for j = 0, #(read_byte_Tb)
                    do
                        read_char_Tb[j + 1] = string.char(read_byte_Tb[j])
                    end
                    read_str = read_str..table.concat(read_char_Tb)
                elseif read_byte_Tb ==nil
                then
                    print(' File read error. please try again! ! !')
                    break;
                end
            end
        end
        file_close()
    end
    return read_str
end

--**********************************************************************
--core

function on_sd_inserted (dir)
    if DISABLED == 0 then
        sd_dir     = dir
        list_dir(sd_dir)

    end
end

--**********************************************************************

function print_symbols ()

    for symbol,value in pairs(_G) do
        print(symbol,value)
        feed_the_dog()
    end
    
end
    
--**********************************************************************

function error1 (text)

    local atime = App.time('')
    local dtime = string.sub(App.date('',"%y%m%d",atime),3,8) .. "_" .. App.date('',"%H%M%S",atime)
    local text1 = text  --   .. "\n" .. debug.traceback()
    my_write_filedata1("debug3.txt",sd_dir..'/'.."debug3.txt",dtime .. ": " .. text1 .."\n",add_write)
    
end
    
--**********************************************************************

function error2 (text)

    if SERIALNUMBER == "9999999" then
        local atime = App.time('')
        local dtime = string.sub(App.date('',"%y%m%d",atime),3,8) .. "_" .. App.date('',"%H%M%S",atime)
        local text1 = text  --   .. "\n" .. debug.traceback()
        my_write_filedata1("debug5.txt",sd_dir..'/'.."debug5.txt",dtime .. ": " .. text1 .."\n",add_write)
    end
    return(0)
    
end
    
--**********************************************************************

function error2_shift ()

    local debug5 = my_read_filedata1('debug5.txt',sd_dir..'/debug5.txt')
    if debug5 == nil then
        debug5 = ""
    end

    local atime = App.time()
    local dtime = string.sub(App.date('',"%y%m%d",atime),3,8) .. "_" .. App.date('',"%H%M%S",atime)
    my_write_filedata1("debug5__" .. dtime .. ".txt",sd_dir..'/'.."debug5__" .. dtime .. ".txt",debug5.."\n",over_write)

    my_write_filedata1("debug5.txt",sd_dir..'/'.."debug5.txt","DEBUG5\n",over_write)

end

--**********************************************************************

function normal_signs (text)

    local text1 = string.gsub(text,"[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,;:-=+_.#)(!] \r\n","")
    return("")
--    return(text1)

end

--**********************************************************************

function feed_the_dog ()
    if file_open ~= nil then
        feed_dog()
    end
end

--**********************************************************************

function on_list_dir (path,filename)

    feed_the_dog()
    if path == sd_dir then
        table.insert(sd_subdirs,filename)
--        list_dir(path.."/"..filename)
        if string.sub(filename,1,5) == 'patch' then
            start_timer(11,500,0,1)
            PATCH_filename = filename
            ladebalken = 5
            set_text(25,5,ladebalken)
        end
    end
    
--    table.insert(all_files,path.."/"..filename)    
    

end

--**********************************************************************
--core

function load_patch (filename)

    stop_timer(10)
    ladebalken = ladebalken + 5
    set_text(25,1,ladebalken)
    patch_file = filename
    local patch_enc = ""

    PATCH_plaintext   = ''
    PATCH_parts       = 0
    PATCH_zaehler     = 0
    PATCH_fortschritt = 1
    PATCH_nr          = 0
    PATCH_zeilen      = {}
    feed_the_dog()
    
    if file_open == nil then

        local file1 = io.open(filename)
        print("LOAD PARTS",filename)
        local file2 = ""
        while 0 == 0 do
            if file1 == nil then
                break
            end
            local line1 = file1:read()
            if line1 == nil then
                file1:close()
                break
            end
            file2 = file2 .. line1 .. "\n"
        end

        patch_enc = file2

    else
--        set_text(14,11,sd_dir..'/'..filename)
        patch_enc = my_read_filedata(sd_dir..'/'..filename)
    end

    feed_the_dog()
    for zeile in string.gmatch(patch_enc,'[^\n]+') do
        table.insert(PATCH_zeilen,zeile)
    end

    PATCH_offset = ladebalken or 0
    PATCH_rest   = 23
    start_timer(12,1,0,1)

end

--*********************************************************************
--core

function load_parts ()

    stop_timer(10)
    feed_the_dog()
    local ciph = ''

    PATCH_nr = PATCH_nr + 1
    while PATCH_zaehler < #PATCH_zeilen do
        feed_the_dog()
        PATCH_zaehler = PATCH_zaehler + 1
        local zeile = PATCH_zeilen[PATCH_zaehler]
        if string.sub(zeile,1,2) == "--" then
            if PATCH_parts == 0 then
                PATCH_parts = PATCH_parts + tonumber(string.sub(zeile,3))
            else
                local chunk = ""
                if #ciph > 20 then
                   chunk = App.decrypt('',sec_key1,trim(ciph))
                end
                feed_the_dog()
                PATCH_plaintext   = PATCH_plaintext .. chunk
                ciph        = ''
                ladebalken = math.floor((100-PATCH_offset-PATCH_rest)*PATCH_zaehler/#PATCH_zeilen) + PATCH_offset
                set_value(25,1,ladebalken)
                break
            end
        else
            ciph = ciph .. zeile .. "\n"
        end
    end

    if PATCH_zaehler == #PATCH_zeilen and #PATCH_plaintext > 20 then
--        set_text(14,11,string.sub(PATCH_plaintext,10,25))
        feed_the_dog()
        patch_call = load(PATCH_plaintext)
        feed_the_dog()
        GIT_COMMIT0           = GIT_COMMIT  --   der git commit der Basis Version ist nun in GIT_COMMIT0
        local SAVE_sd_dir     = sd_dir      --   wird durch den nachfolgenden patch_call ggfs. ueberschrieben
        local SAVE_sd_subdirs = sd_subdirs
        patch_call()
        PATCH_plaintext       = ""
        feed_the_dog()
        sd_dir     = SAVE_sd_dir
        sd_subdirs = SAVE_sd_subdirs
        ladebalken = 77        
        start_timer(10,1,0,1)
    else
        start_timer(12,100,0,1)
    end
    
end

--*********************************************************************

function trim (text)
    return (text:gsub('^%s*(.-)%s*$', '%1'))
end



--*************************************************************************
--  CLASSES
--*************************************************************************
--private

App          = {}
App.__index  = App

--*************************************************************************
--system

function App:new (simulation)

    local self        = setmetatable({},App)
    self.simulation   = simulation
    
    if self.simulation ~= 1 then
        set_text(25,1,0)
        change_screen(25)  -- warte auf das Laden der Hotfixes
--        ladebalken          = 0
        ladebalken_interval = 8  --  x 100  Millisekunden Boot time, weil 100 Runden
        start_timer(10,1,0,1)
    else
        if App.init ~= nil then
            self:init()
        end
    end
    
    local xx = 1
    
    return self
    
end

--**********************************************************************

function App.on_timer_pre (self,tid)

    if tid == 10 then  --  Startverzoegerung, damit patches geladen werden koennen
        if ladebalken > 99 then
            if App.init ~= nil then
                feed_the_dog()
                self:init()
            end
        else
            ladebalken = ladebalken + 1
            set_value(25,1,ladebalken)
            feed_the_dog()
            start_timer(10,ladebalken_interval,0,1)
        end
--    elseif tid == 12 then
--       set_value(25,1,PATCH_fortschritt)
    else
        return self:on_timer(tid)
    end

end

--**********************************************************************

function App.decrypt  (self,ckey,text)

    if text == nil then
        return ''
    end
    
    local text1 = string.gsub(text, "\r","")
    text1       = string.gsub(text1,"\n","")
    
    local text2 = bin.hextos(text1)
    if #text2 < 16 then
        return(text)
    end
    
    
    local erg = ''
    if #ckey == 32 then
        local ckey1 = bin.hextos(ckey)
        erg         = rc4.decrypt(ckey1,text2)
    elseif #ckey == 64 then
        local ckey1 = bin.hextos(ckey)
        erg         = salsa20.decrypt(ckey1,1,bin.hextos('1234567890abcdef'),text2)
    else
        local ckey1 = bin.hextos( string.sub(ckey,1,64) )
        local ckey2 = bin.hextos( string.sub(ckey,65)   )
--        print(ckey1,ckey2)
        erg         = crypto.box_open(text2,string.sub(SERIALNUMBER..'00000000000000000000',1,24),
                                      ckey1,ckey2)
--    print(1234)
    end
    
    if erg == '' then
        return(text)
    end

    return erg

end

--**********************************************************************

function App.crypt (self,ckey,text,zeilenlaenge)

    if text == nil then
        return ''
    end
    
--    if #text < 32 then
--        return text
--    end

    local erg = ''
    if #ckey == 32 then
        local ckey1 = bin.hextos(ckey)
        error2("32crypt")
        erg         = rc4.encrypt(ckey1,text)
    elseif #ckey == 64 then
        local ckey1 = bin.hextos(ckey)
        error2("64crypt")
        erg         = salsa20.encrypt(ckey1,1,bin.hextos('1234567890abcdef'),text)
    else
        error2("96crypt")
        local ckey1 = bin.hextos( string.sub(ckey,1,64) )
        local ckey2 = bin.hextos( string.sub(ckey,65)   )
        erg         = crypto.box(text,string.sub(SERIALNUMBER..'00000000000000000000',1,24),
                             ckey1,ckey2)
    end
    
--    if zeilenlaenge ~= nil then   --  dieser Code funktioniert nicht! Der Loader bleibt haengen!
--        if zeilenlaenge > 0 then
            erg = bin.stohex(erg)
--        else
--            erg = base27_encode(erg)
--            zeilenlaenge = -zeilenlaenge
--        end
--    end
    
    
    erg1 = ""
    while zeilenlaenge ~= nil do
--        error2("99crypt"..tostring(zeilenlaenge).."  "..tostring(#erg))
        erg1 = erg1 .. string.sub(erg,1,zeilenlaenge) .. "\n"
        erg  = string.sub(erg,zeilenlaenge+1)
        if #erg == 0 then
            return(erg1.."\n")
        end
    end

    
    return erg

end

--**********************************************************************

function App.save_settings (self)

    local text = ''
        
    text = text .. "T1:" .. self.price_per_hour[1]:read() .. "," .. self.min_sum[1]:read() .. "," .. self.max_park_hour[1]:read() .. "," ..
           self.max_park_minute[1]:read() .. self.tagesticket[1]:read() .. "," .. self.pay_mode[1]:read() .. "," .. self.tax_vat[1]:read() .. "\n"
    text = text .. "T2:" .. self.price_per_hour[2]:read() .. "," .. self.min_sum[2]:read() .. "," .. self.max_park_hour[2]:read() .. "," ..
           self.max_park_minute[2]:read() .. self.tagesticket[2]:read() .. "," .. self.pay_mode[2]:read() .. "," .. self.tax_vat[2]:read() .. "\n"
    text = text .. "CF:"   .. self.max_zeilen:read() .. "," .. self.report_number:read() .. "\n"
    text = text .. "L1:"   .. self.location1() .. "\n"
    text = text .. "L2:"   .. self.location2() .. "\n"
    text = text .. "L3:"   .. self.location3() .. "\n"
    text = text .. "LANG:" .. self.lang_description1() .. "," .. self.lang_description2() .. "," .. self.lang_description3() .. "\n"
    text = text .. "PIN:"  .. self.pin_definitions() .. "\n"    
    text = text .. "TARF:"    
    for i = 1,10,1 do
        text = text .. self.tarif_par[1][i]:read() .. "," .. self.tarif_par[2][i]:read() .. "," 
                    .. self.tarif_par[3][i]:read() .. "," .. self.tarif_par[4][i]:read() .. "," 
    end
    text = text .. "\n"

    return(text)
    
    
end


--**********************************************************************

function App.init (self)


    if self.simulation == 1 then
        local statf  = io.open('/proc/self/stat')
        math.randomseed(self:time()+os.clock()+tonumber(string.sub(statf:read(),1,4)))
        statf:close()
    else
        init_time = self:time()
        math.randomseed(init_time)
        self.comment = 'START_COMMENT  '
        self.counter = nil
        self:no_simulation()
    end
    
    self.sign_idx = string.format("%4u",self:time())
    CHECK_SIGN    = "sign_" .. self.sign_idx .. ".txt"

    feed_the_dog()
    self.debug3 = my_read_filedata1('debug3.txt',sd_dir..'/debug3.txt')
    if self.debug3 == nil then
        self.debug3 = ""
    end
--    self.debug3 = "vvvv"

    if #self.debug3 > 10 then
        local atime = self:time()
        local dtime = string.sub(self:date("%y%m%d",atime),3,8) .. "_" .. self:date("%H%M%S",atime)
        my_write_filedata1("debug3__" .. dtime .. ".txt",sd_dir..'/'.."debug3__" .. dtime .. ".txt",self.debug3.."\n",over_write)
    end    
    feed_the_dog()

    my_write_filedata1("debug3.txt",sd_dir..'/'.."debug3.txt","DEBUG\n",over_write)
    PRINT_BF_EXIST = string.sub( VOUCHER_SIGNA_TABLE, VOUCHER_M*121, VOUCHER_M*121+5 )
    
    feed_the_dog()
    
    self:init1()
    
end

--**********************************************************************

function App.init1 (self) local s, r = xpcall(App.p_init1,function(e) error1(e) end,self) return(r) end

--**********************************************************************
 
function App.p_init1 (self)

    self.uart_sperre             = 1
    self.field_sim               = {}
    self.screens                 = {}
    self.fields                  = {}
    self.cache_langcode_to_nr    = {}
    XFILES                       = {}
    for i,file in ipairs(sd_subdirs) do
        if string.sub(file,1,1) == "x" or string.sub(file,1,1) == "X" then
            table.insert(XFILES,file)  --  Unterverzeichnisse mit verschluesselten Dateien
            list_dir(file)
        end
    end
    feed_the_dog()
    

    self.press_release           = 1   -- 0: on Press, 2: on Release mit long press, 1: on Press mit long (zwei Ereignisse)
                                       -- 11: if 1 occurs on a screen, set it to 2 for the next times. 

    self.machine_is_running      = 0

    if LANGUAGES == nil then
        LANGUAGES = {"DE","EN","FR"}
        FLAGS     = {"DE","EN","FR"}
    end


    self.main                    = Screen:new(self, 0)
    if file_open == nil then
        self.standby                 = Screen:new(self,1)
    else
        self.standby                 = Screen:new(self,19)    
    end
    self.standby.pr_re = 2
    self.lang_layout3            = Screen:new(self, 2, {  171, 270, 271, } )
    self.coin                    = Screen:new(self, 3, {  171, 271, 270, 267, 277, 278, 279, 201, 204, } )
    self.print_msg               = Screen:new(self, 4, { } )
    self.admin                   = Screen:new(self, 5, {  217, 202, 201, 212, 213, 205, 299, 204, } )
    self.adm_tarif               = Screen:new(self, 6, {  203, 208, 201, 204, 207, 208, 211, 215, 216, 217, 218, 299, 206, 219, 220, 221, 222, 233, 240, } )
    self.adm_ext                 = Screen:new(self, 7, {  204, 299, 201, 212, 203, 208, } )
    self.adm_basic               = Screen:new(self, 8, {  202, 203, 204, 207, 206, 299, 205, 211, 215, } )
    self.adm_proto               = Screen:new(self, 9, {  213, 205, 299, 203, 202, 204, } )
    self.not_ready               = Screen:new(self,10, {  270, } )    --  Gerät ist nicht betriebsbereit Screen
    self.adm_lang_sel            = Screen:new(self,11, {  280, 299, } )
    self.pin                     = Screen:new(self,12, {  280, 202, } )
    self.invalid_pin             = Screen:new(self,13, { } )
    self.ticketslot              = Screen:new(self,14, {  280, 207, 217, } )
    self.lang_layout2            = Screen:new(self,15, {  171, 270, 271, } )
    self.lang_layout1            = Screen:new(self,16, {  171, 270, 271, } )
    self.pin_admin               = Screen:new(self,18, {  262, 299, } )
    self.pin_template            = Screen:new(self,20, { } )
    self.adm_pin_new             = Screen:new(self,21, {  201, 204, 299, 270, 201, 217, 205, } )
    self.adm_tarif2              = Screen:new(self,22, {  203, 208, 201, 204, 207, 208, 211, 215, 216, 217, 218, 299, 206, 219, 220, 221, 222, 233, 240, } )
    self.adm_device              = Screen:new(self,23, {  220, 217, 218, 203, 202, 204, 205, 206, 207, 299, 201, } )
    self.adm_soft_reset          = Screen:new(self,24, { } )
    self.start_screen            = Screen:new(self,25, { } )
    self.no_sdcard               = Screen:new(self,26, {  270, } )
    self.qrcode                  = Screen:new(self,27, {  3, 5, 299 } )
    self.adm_tarif_def           = Screen:new(self,28, {  204, 215, 216, 217, 218, 299 } )
    self.adm_tarif_def2          = Screen:new(self,29, {  204, 215, 216, 217, 218, 299 } )


    self.debug1                  = Field:new(self,'')
    self.debug1:add_field(self.standby,200)        --  das ist der gruene Debug-Balken, der aktiviert werden kann
    self.debug1:add_field(self.lang_layout3,200)
    self.debug1:add_field(self.coin,200)
    self.debug1:add_field(self.print_msg,200)
    self.debug1:add_field(self.admin,200)
    self.debug1:add_field(self.adm_tarif,200)
    self.debug1:add_field(self.adm_ext,200)
    self.debug1:add_field(self.adm_basic,200)
    self.debug1:add_field(self.adm_proto,200)
    self.debug1:add_field(self.not_ready,200)
    self.debug1:add_field(self.adm_lang_sel,200)
    self.debug1:add_field(self.pin,200)
    self.debug1:add_field(self.ticketslot,200)
    self.debug1:add_field(self.pin_admin,200)
    self.debug1:add_field(self.adm_pin_new,200)
    self.debug1:add_field(self.adm_tarif,200)
    self.debug1:add_field(self.adm_tarif2,200)
    self.debug1:add_field(self.adm_tarif_def,200)
    self.debug1:add_field(self.adm_tarif_def2,200)
    self.debug1:add_field(self.no_sdcard,200)
    feed_the_dog()

    self.model_nr = 1
    if     SERIALNUMBER == '0125377' then self.model_nr = 0
    elseif SERIALNUMBER == '0132951' then self.model_nr = 0
    elseif SERIALNUMBER == '0170833' then self.model_nr = 0
    elseif SERIALNUMBER == '0171393' then self.model_nr = 0
    elseif SERIALNUMBER == '0221177' then self.model_nr = 0
    elseif SERIALNUMBER == '0221543' then self.model_nr = 0
    elseif SERIALNUMBER == '0225791' then self.model_nr = 0
    elseif SERIALNUMBER == '0244179' then self.model_nr = 0
    elseif SERIALNUMBER == '0253954' then self.model_nr = 0
    elseif SERIALNUMBER == '0332942' then self.model_nr = 0
    elseif SERIALNUMBER == '0345748' then self.model_nr = 0
    elseif SERIALNUMBER == '0353117' then self.model_nr = 0
    elseif SERIALNUMBER == '0421683' then self.model_nr = 0
    elseif SERIALNUMBER == '0432779' then self.model_nr = 0
    elseif SERIALNUMBER == '0432781' then self.model_nr = 0
    elseif SERIALNUMBER == '0437221' then self.model_nr = 0
    elseif SERIALNUMBER == '0510487' then self.model_nr = 0
    elseif SERIALNUMBER == '0518898' then self.model_nr = 0
    elseif SERIALNUMBER == '0541273' then self.model_nr = 0
    elseif SERIALNUMBER == '0553227' then self.model_nr = 0
    elseif SERIALNUMBER == '0579312' then self.model_nr = 0
    elseif SERIALNUMBER == '0614825' then self.model_nr = 0
    elseif SERIALNUMBER == 'xxx0622171' then self.model_nr = 0   --  Frickenhausen
    elseif SERIALNUMBER == '0635710' then self.model_nr = 0
    elseif SERIALNUMBER == '0717833' then self.model_nr = 0
    elseif SERIALNUMBER == '0722615' then self.model_nr = 0
    elseif SERIALNUMBER == '0742293' then self.model_nr = 0
    elseif SERIALNUMBER == '0746291' then self.model_nr = 0
    elseif SERIALNUMBER == '0769931' then self.model_nr = 0
    elseif SERIALNUMBER == '0789641' then self.model_nr = 0
    elseif SERIALNUMBER == '0802931' then self.model_nr = 0
    elseif SERIALNUMBER == '9999999' then self.model_nr = 0
    end 

    self:init2()

end

--**********************************************************************

function App.init2 (self)

    self.reset_device        = 0        --  button 17
    self.reset_device_ja     = Field:new(self,'', self.adm_device,218)
    self.reset_device_nein   = Field:new(self,'', self.adm_device,217)

    self.reset_ok_code           = Field:new(self,'', self.adm_device,12)
    self.ser_number              = Field:new(self,'0',self.adm_device,21)
    self.git_version             = Field:new(self,'0',self.adm_device,22)
    self.patchv                  = Field:new(self,'none',self.adm_device,23)
    self.firmware                = Field:new(self,'1.0',self.adm_device,24)
    self.screen_nr               = Field:new(self,'1.0',self.adm_device,25)
    self.ueberdeckung            = Field:new(self,'')
    self.ueberdeckung:add_field(self.lang_layout3,94)
    self.ueberdeckung:add_field(self.lang_layout2,94)
    self.ueberdeckung:add_field(self.lang_layout1,94)
    self.ueberdeckung:add_field(self.coin,94)
    self.ueberdeckung:add_field(self.pin,94)
    self.ueberdeckung:add_field(self.qrcode,94)
    self.ueberdeckung:set_visiable(-1)
    
    self.show_low                = Field:new(self,'')
    self.show_low:add_field(self.lang_layout3,98)
    self.show_low:add_field(self.lang_layout2,98)
    self.show_low:add_field(self.lang_layout1,98)
    self.show_low:add_field(self.coin,98)
    self.show_low:add_field(self.qrcode,98)
    self.show_low:set_visiable(1)

    self.text1                   = Field:new(self,'')
    self.text1:add_field(self.coin,270)

    self.text2                   = Field:new(self,'')
    self.text2:add_field(self.coin,277)

    self.text3                   = Field:new(self,'')
    self.text3:add_field(self.coin,278)

    self.text4                   = Field:new(self,'')
    self.text4:add_field(self.coin,279)

    self.muenzen                 = Field:new(self,'')
    self.muenzen:add_field(self.lang_layout3,82)
    self.muenzen:add_field(self.lang_layout2,82)
    self.muenzen:add_field(self.lang_layout1,82)
    self.muenzen:add_field(self.coin,82)
    self.muenzen:add_field(self.lang_layout3,83)
    self.muenzen:add_field(self.lang_layout2,83)
    self.muenzen:add_field(self.lang_layout1,83)
    self.muenzen:add_field(self.coin,83)
    self.muenzen:add_field(self.lang_layout3,84)
    self.muenzen:add_field(self.lang_layout2,84)
    self.muenzen:add_field(self.lang_layout1,84)
    self.muenzen:add_field(self.coin,84)

    self.muenzen1                = Field:new(self,'20 Ct')
    self.muenzen1:add_field(self.lang_layout3,282)
    self.muenzen1:add_field(self.lang_layout2,282)
    self.muenzen1:add_field(self.lang_layout1,282)
    self.muenzen1:add_field(self.coin,282)

    self.muenzen2                = Field:new(self,'50 Ct')
    self.muenzen2:add_field(self.lang_layout3,283)
    self.muenzen2:add_field(self.lang_layout2,283)
    self.muenzen2:add_field(self.lang_layout1,283)
    self.muenzen2:add_field(self.coin,283)

    self.muenzen3                = Field:new(self,'1 Euro')
    self.muenzen3:add_field(self.lang_layout3,284)
    self.muenzen3:add_field(self.lang_layout2,284)
    self.muenzen3:add_field(self.lang_layout1,284)
    self.muenzen3:add_field(self.coin,284)
    
    self.bargeldlos              = Field:new(self,'')
    self.bargeldlos:add_field(self.lang_layout3,171)
    self.bargeldlos:add_field(self.lang_layout2,171)
    self.bargeldlos:add_field(self.lang_layout1,171)
    self.bargeldlos:add_field(self.coin,171)
    
    self.shortinfo               = Field:new(self,'')
    self.shortinfo:add_field(self.lang_layout3,170)
    self.shortinfo:add_field(self.lang_layout2,170)
    self.shortinfo:add_field(self.lang_layout1,170)
    self.shortinfo:add_field(self.coin,170)
    
    self.knob1                   = Field:new(self,'')
    self.knob1:add_field(self.lang_layout3,95)
    self.knob1:add_field(self.lang_layout2,95)
    self.knob1:add_field(self.lang_layout1,95)
    self.knob1:add_field(self.coin,95)

    self.knob2                   = Field:new(self,'')
    self.knob2:add_field(self.lang_layout3,96)
    self.knob2:add_field(self.lang_layout2,96)
    self.knob2:add_field(self.lang_layout1,96)
    self.knob2:add_field(self.coin,96)
    

    self.sondertarif_anzeige     = Field:new(self,'',self.coin,3)
    self.nochmal_button          = Field:new(self,'',self.coin,4)
    self.nochmal                 = Field:new(self,'',self.coin,204)
    self.tarifnr                 = 1
    self.pin_feld                = Field:new(self,'',self.pin,1)


    self.price_per_hour          = { Field:new(self,DEFAULT_price_per_hour_1), Field:new(self,DEFAULT_price_per_hour_2) } 
    self.price_per_hour[1]:add_field(self.adm_tarif,12)
    self.price_per_hour[1]:add_flash_position(50,10)
    self.price_per_hour[2]:add_field(self.adm_tarif2,12)
    self.price_per_hour[2]:add_flash_position(850,10)

    self.min_sum             = { Field:new(self,DEFAULT_min_sum_1), Field:new(self,DEFAULT_min_sum_2) }
    self.min_sum[1]:add_field(self.adm_tarif,9)
    self.min_sum[1]:add_flash_position(70,10)
    self.min_sum[2]:add_field(self.adm_tarif2,9)
    self.min_sum[2]:add_flash_position(870,10)

    self.max_park_hour       = { Field:new(self,DEFAULT_max_park_hour_1), Field:new(self,DEFAULT_max_park_hour_2) }
    self.max_park_hour[1]:add_field(self.adm_tarif,10)
    self.max_park_hour[1]:add_flash_position(90,5)
    self.max_park_hour[2]:add_field(self.adm_tarif2,10)
    self.max_park_hour[2]:add_flash_position(890,5)

    self.max_park_minute     = { Field:new(self,DEFAULT_max_park_minute_1),  Field:new(self,DEFAULT_max_park_minute_2) }
    self.max_park_minute[1]:add_field(self.adm_tarif,14)
    self.max_park_minute[1]:add_flash_position(100,5)
    self.max_park_minute[2]:add_field(self.adm_tarif2,14)
    self.max_park_minute[2]:add_flash_position(900,5)

    self.tagesticket         = { Field:new(self,DEFAULT_tagesticket_1),  Field:new(self,DEFAULT_tagesticket_2) }   --  button 17
    self.tagesticket[1]:add_flash_position(110,5)
    self.tagesticket[2]:add_flash_position(910,5)

    self.tagesticket_ja      = { Field:new(self,''),  Field:new(self,'') }
    self.tagesticket_ja[1]:add_field(self.adm_tarif,218)
    self.tagesticket_ja[2]:add_field(self.adm_tarif2,218)

    self.tagesticket_nein    = { Field:new(self,'0'),  Field:new(self,'0') }
    self.tagesticket_nein[1]:add_field(self.adm_tarif,217)
    self.tagesticket_nein[2]:add_field(self.adm_tarif2,217)

    self.pay_mode            = { Field:new(self,DEFAULT_pay_mode_1),  Field:new(self,DEFAULT_pay_mode_2) }  --  button 3
    self.pay_mode[1]:add_flash_position(120,5)     --  Werte:  0 / 1 / 2 
    self.pay_mode[2]:add_flash_position(920,5)

    self.pay_mode_coins      = { Field:new(self,'0'),  Field:new(self,'0') }
    self.pay_mode_coins[1]:add_field(self.adm_tarif,220)
    self.pay_mode_coins[2]:add_field(self.adm_tarif2,220)

    self.pay_mode_qrcode     = { Field:new(self,'0'),  Field:new(self,'0') }
    self.pay_mode_qrcode[1]:add_field(self.adm_tarif,221)
    self.pay_mode_qrcode[2]:add_field(self.adm_tarif2,221)

    self.pay_mode_both       = { Field:new(self,'0'),  Field:new(self,'0') }
    self.pay_mode_both[1]:add_field(self.adm_tarif,222)
    self.pay_mode_both[2]:add_field(self.adm_tarif2,222)

    self.tax_vat                 = Field:new(self,DEFAULT_tax_vat)
    self.tax_vat:add_field(self.adm_tarif,2)
    self.tax_vat:add_field(self.adm_tarif2,2)
    self.tax_vat:add_flash_position(130,10)

    self.max_zeilen              = Field:new(self,DEFAULT_max_zeilen)
    self.max_zeilen:add_field(self.adm_proto,5)
    self.max_zeilen:add_flash_position(280,10)

    self.reportnumber            = Field:new(self,DEFAULT_reportnumber)
    self.reportnumber:add_field(self.adm_proto,6)
    self.reportnumber:add_flash_position(290,10)

    self.report_wird_gedruckt    = Field:new(self,'',self.adm_proto,205)

    self.is_tarif2               = Field:new(self,'', self.adm_tarif,20)

    self.location1               = Field:new(self,DEFAULT_location1)
    self.location1:add_field(self.adm_basic,14)
    self.location1:add_flash_position(140,10)

    self.location2               = Field:new(self,DEFAULT_location2)
    self.location2:add_field(self.adm_basic,10)
    self.location2:add_flash_position(180,10)

    self.location3               = Field:new(self,DEFAULT_location3)
    self.location3:add_field(self.adm_basic,15)
    self.location3:add_flash_position(220,10)

    self.lang_description1       = Field:new(self,'')
    self.lang_description1:add_field(self.lang_layout3,4)
    self.lang_description1:add_field(self.lang_layout2,4)
    self.lang_description1:add_field(self.lang_layout1,4)

    self.lang_description2       = Field:new(self,'')
    self.lang_description2:add_field(self.lang_layout3,5)
    self.lang_description2:add_field(self.lang_layout2,5)

    self.lang_description3       = Field:new(self,'')
    self.lang_description3:add_field(self.lang_layout3,6)
    feed_the_dog()



    self.country_table = {}
    local zaehler = 0
    while zaehler < 20 do
        zaehler = zaehler + 1
        if FLAGS[zaehler] == nil or zaehler > #LANGUAGES then
            table.insert(self.country_table," "..string.sub("1234567890abcdefghijk",zaehler,zaehler))  --  man muss
        else        --  die Sprachen unterscheiden, auch wenn das stillgelegte Dummy-Sprachen sind
            if FLAGS[zaehler] == "UK" then
                table.insert(self.country_table,"EN")
            else
                table.insert(self.country_table,FLAGS[zaehler])
            end
        end
    end
    
    self.ldescriptions = {}

    self.lang1 = {}
    self.lang2 = {}
    self.lang3 = {}


    local first_three_languages  = ""
    for i,lang in ipairs( self.country_table ) do


        self.lang1[lang] = Field:new(self,lang)
        self.lang1[lang]:add_field(self.lang_layout3, 9+i)
        self.lang1[lang]:add_field(self.lang_layout2, 9+i)
        self.lang1[lang]:add_field(self.lang_layout1, 9+i)
        
        self.lang2[lang] = Field:new(self,lang)
        self.lang2[lang]:add_field(self.lang_layout3, #self.country_table+9+i)
        self.lang2[lang]:add_field(self.lang_layout2, #self.country_table+9+i)
        
        self.lang3[lang] = Field:new(self,lang)
        self.lang3[lang]:add_field(self.lang_layout3, 2*#self.country_table+9+i)

--        if LANGUAGE_FILTER == "ALL" or string.find(","..LANGUAGE_FILTER..",",","..lang..",") then
        first_three_languages = first_three_languages .. lang
        if string.sub(lang,1,1) == " " then  --  dummy language, starting with blank
            self.adm_lang_sel:set_text(20+#first_three_languages/2,"  ")
        else
            table.insert(self.ldescriptions,t("LANG",i))
            self.adm_lang_sel:set_text(20+#first_three_languages/2,lang)
        end
               
--        end
        
--        if lang == string.sub(self.lang_selector.default_text,1,2) and self.simulation == 0 then
--            set_language(i-1)
--            lang_nr = i+1
--        end
    end
    self.flags_set = ''


    self.lang_selector = Field:new(self,string.sub(first_three_languages,1,6))
    self.lang_selector:add_flash_position(260,10)
    self.reportlang    = Field:new(self,string.sub(first_three_languages,1,2))
    self.reportlang:add_flash_position(273,4)
    self.reportlang:add_field(self.adm_basic,216)
    self.reportlang1   = Field:new(self,'Protokolle:')
    self.reportlang1:add_field(self.adm_basic,215)

--    set_text(1,200,"hier1243 " .. "")
--    change_screen(1) end function xxx ()

--    self.ticketslot_pin          = Field:new(self,'',  self.ticketslot,2)

    self.pin_start               = Field:new(self,'',self.adm_pin_new,6)

    self.ticketslot_pin          = Field:new(self,'')
    self.ticketslot_pin:add_flash_position(310,100)
    
    self.ticketslot_tan1         = Field:new(self,'',  self.ticketslot,1)
    self.ticketslot_tan2         = Field:new(self,'',  self.ticketslot,3)
    self.ticketslot_tan3         = Field:new(self,'',  self.ticketslot,4)
    self.anzeige_betrag          = Field:new(self,'0,00', self.coin,73)
    self.anzeige_zusatztag       = Field:new(self,'0,00', self.coin,54)
    self.anzeige_drucken         = Field:new(self,'',self.coin,65)
    self.anzeige_parkdauer       = Field:new(self,'',self.coin,72)

    self.qrcode_text             = Field:new(self,'',self.qrcode,1)   --  in dieses Feld kann der QR-Code eingetragen werden
    self.qrtan                   = Field:new(self,'',self.qrcode,2)
    self.qr_invalid              = Field:new(self,'',self.qrcode,3)

    self.qrtaste                 = {}
    for nr = 1,12,1 do
        self.qrtaste[nr] = Field:new(self,'',self.qrcode,nr+9)
    end
    
    Field:new(self,'',self.qrcode,3)

    if file_open == nil then
        self.anzeige_parkdauer:add_field(self.standby,17)
        self.parkzeit                = Field:new(self,'',  self.standby,13)
        self.header_message          = Field:new(self,'',  self.standby,23)
        self.pluse_sum               = Field:new(self,'',  self.standby,2)
        self.press_button            = Field:new(self,'',  self.standby,21)
    end


    self.setup_lang1             = Field:new(self,'',  self.adm_basic,20)
    self.setup_lang2             = Field:new(self,'',  self.adm_basic,21)
    self.setup_lang3             = Field:new(self,'',  self.adm_basic,22)

    self.pin_definitions         = Field:new(self,DEFAULT_pin_definitions)

    self.pin_definitions:add_flash_position(15*4096+5,4000)

    self.pin_offset              = 0
    self.ungueltig_sec           = Field:new(self,'',       self.pin,33)
    self.ungueltige_pin          = Field:new(self,'',       self.pin,3)
    self.ungueltig_text          = Field:new(self,'Ungültige PIN',  self.pin,202)
    self.pin_wartezeit           = 0
    feed_the_dog()

    self.pin_entries = {}
    while #self.pin_entries < 10 do
        table.insert(self.pin_entries, {
                       Field:new(self,'',  self.pin_admin,#self.pin_entries+1),
                       Field:new(self,'',  self.pin_admin,#self.pin_entries+21),
                       Field:new(self,'',  self.pin_admin,#self.pin_entries+51)
                    } )
    end
    
--    if string.sub(self.location1:read(),1,1) == "+" then
--        function print (text)
--            my_write_filedata1("debug3.txt",sd_dir..'/'.."debug3.txt",text.."\n",add_write)
----            beep(100)
--        end
--    end

    self:init3()
    
end

--**********************************************************************

function App.init3 (self)

    self.tarif_par = { {}, {}, {}, {} }

    self.tarif_par[1][1] = Field:new(self,DEFAULT_tarif_1_1,self.adm_tarif_def ,20+1)
    self.tarif_par[2][1] = Field:new(self,DEFAULT_tarif_2_1,self.adm_tarif_def ,40+1)
    self.tarif_par[3][1] = Field:new(self,DEFAULT_tarif_3_1,self.adm_tarif_def2,20+1)
    self.tarif_par[4][1] = Field:new(self,DEFAULT_tarif_4_1,self.adm_tarif_def2,40+1)
        
    self.tarif_par[1][2] = Field:new(self,DEFAULT_tarif_1_2,self.adm_tarif_def ,20+2)
    self.tarif_par[2][2] = Field:new(self,DEFAULT_tarif_2_2,self.adm_tarif_def ,40+2)
    self.tarif_par[3][2] = Field:new(self,DEFAULT_tarif_3_2,self.adm_tarif_def2,20+2)
    self.tarif_par[4][2] = Field:new(self,DEFAULT_tarif_4_2,self.adm_tarif_def2,40+2)
        
    self.tarif_par[1][3] = Field:new(self,DEFAULT_tarif_1_3,self.adm_tarif_def ,20+3)
    self.tarif_par[2][3] = Field:new(self,DEFAULT_tarif_2_3,self.adm_tarif_def ,40+3)
    self.tarif_par[3][3] = Field:new(self,DEFAULT_tarif_3_3,self.adm_tarif_def2,20+3)
    self.tarif_par[4][3] = Field:new(self,DEFAULT_tarif_4_3,self.adm_tarif_def2,40+3)
        
    self.tarif_par[1][4] = Field:new(self,DEFAULT_tarif_1_4,self.adm_tarif_def ,20+4)
    self.tarif_par[2][4] = Field:new(self,DEFAULT_tarif_2_4,self.adm_tarif_def ,40+4)
    self.tarif_par[3][4] = Field:new(self,DEFAULT_tarif_3_4,self.adm_tarif_def2,20+4)
    self.tarif_par[4][4] = Field:new(self,DEFAULT_tarif_4_4,self.adm_tarif_def2,40+4)
        
    self.tarif_par[1][5] = Field:new(self,DEFAULT_tarif_1_5,self.adm_tarif_def ,20+5)
    self.tarif_par[2][5] = Field:new(self,DEFAULT_tarif_2_5,self.adm_tarif_def ,40+5)
    self.tarif_par[3][5] = Field:new(self,DEFAULT_tarif_3_5,self.adm_tarif_def2,20+5)
    self.tarif_par[4][5] = Field:new(self,DEFAULT_tarif_4_5,self.adm_tarif_def2,40+5)
        
    self.tarif_par[1][6] = Field:new(self,DEFAULT_tarif_1_6,self.adm_tarif_def ,20+6)
    self.tarif_par[2][6] = Field:new(self,DEFAULT_tarif_2_6,self.adm_tarif_def ,40+6)
    self.tarif_par[3][6] = Field:new(self,DEFAULT_tarif_3_6,self.adm_tarif_def2,20+6)
    self.tarif_par[4][6] = Field:new(self,DEFAULT_tarif_4_6,self.adm_tarif_def2,40+6)
        
    self.tarif_par[1][7] = Field:new(self,DEFAULT_tarif_1_7,self.adm_tarif_def ,20+7)
    self.tarif_par[2][7] = Field:new(self,DEFAULT_tarif_2_7,self.adm_tarif_def ,40+7)
    self.tarif_par[3][7] = Field:new(self,DEFAULT_tarif_3_7,self.adm_tarif_def2,20+7)
    self.tarif_par[4][7] = Field:new(self,DEFAULT_tarif_4_7,self.adm_tarif_def2,40+7)
        
    self.tarif_par[1][8] = Field:new(self,DEFAULT_tarif_1_8,self.adm_tarif_def ,20+8)
    self.tarif_par[2][8] = Field:new(self,DEFAULT_tarif_2_8,self.adm_tarif_def ,40+8)
    self.tarif_par[3][8] = Field:new(self,DEFAULT_tarif_3_8,self.adm_tarif_def2,20+8)
    self.tarif_par[4][8] = Field:new(self,DEFAULT_tarif_4_8,self.adm_tarif_def2,40+8)
        
    self.tarif_par[1][9] = Field:new(self,DEFAULT_tarif_1_9,self.adm_tarif_def ,20+9)
    self.tarif_par[2][9] = Field:new(self,DEFAULT_tarif_2_9,self.adm_tarif_def ,40+9)
    self.tarif_par[3][9] = Field:new(self,DEFAULT_tarif_3_9,self.adm_tarif_def2,20+9)
    self.tarif_par[4][9] = Field:new(self,DEFAULT_tarif_4_9,self.adm_tarif_def2,40+9)
        
    self.tarif_par[1][10] = Field:new(self,DEFAULT_tarif_1_10,self.adm_tarif_def ,20+10)
    self.tarif_par[2][10] = Field:new(self,DEFAULT_tarif_2_10,self.adm_tarif_def ,40+10)
    self.tarif_par[3][10] = Field:new(self,DEFAULT_tarif_3_10,self.adm_tarif_def2,20+10)
    self.tarif_par[4][10] = Field:new(self,DEFAULT_tarif_4_10,self.adm_tarif_def2,40+10)

    for i = 1,10,1 do
    
--        self.tarif_par[1][i] = Field:new(self,'',self.adm_tarif_def ,20+i)
--        self.tarif_par[2][i] = Field:new(self,'',self.adm_tarif_def ,40+i)
--        self.tarif_par[3][i] = Field:new(self,'',self.adm_tarif_def2,20+i)
--        self.tarif_par[4][i] = Field:new(self,'',self.adm_tarif_def2,40+i)
        
        self.tarif_par[1][i]:add_flash_position(1200+10*i,10)
        self.tarif_par[2][i]:add_flash_position(1400+10*i,10)
        self.tarif_par[3][i]:add_flash_position(1600+10*i,10)
        self.tarif_par[4][i]:add_flash_position(1800+10*i,10)
        
    end
    
    local actual_language = 999
    for i,country in ipairs(self.country_table) do
        if self.country_table[i] == actual_country then
            actual_language = self.ldescriptions[i]
        end
    end
    feed_the_dog()
    
    self:init4()


end

--**********************************************************************
 
function App.init4 (self)


--  Einrichten von 10 moeglichen Positionen fuer die Flash-Signatur,
--  wegen der Haeufigkeit des Schreibens einer Signatur

    TESTPOINTS = '99999'

    self.signature_positions = {}
    for i = 0,9 do
        table.insert(self.signature_positions,12300+i*4096)
    end


--  Einrichten von zehn Countern, die in Summe den Zaehlerstand ergeben,
--  und heruntergezaehlt werden. Auch hier der Grund die Haeufigkeit 
--  des Schreibens

--  das Geraet geht auf 'Geraet nicht betriebsbereit', wenn keine Punkte mehr da sind, und versucht wird, ein
--  Parkticket zu drucken. Man kann dann wieder anschalten und gleich ins Admin-Menue gehen um sich eine TAN
--  geben zu lassen. Das geht dann noch, und man kann das Geraet mit Punkten wieder aufladen.

    self.counter = {}
    for i = 0,9,1 do
        local count = Field:new(self,'100')   --  ein Punkt defaultmaessig aufgeladen
        if file_open == nil then
            count = Field:new(self,TESTPOINTS)  --  '10000')   --  im Testmodus 10 * TESTPOINTS Punkte verfuegbar
        end
        count:add_flash_position(12600+i*4096,100)
        table.insert(self.counter,count)
    end

    self.aktueller_teilcounter = 0
    self.counter1              = Field:new(self,'0', self.admin,8)

    self.anzahl_gedruckte_parktickets = Field:new(self,'0', self.admin,7)
    self.anzahl_gedruckte_parktickets:write('0')

    self:make_tarif_table()   --   geht leider nicht in init3(), da in diesem Befehl evtl. schon in den Flash geschrieben wird

--    for f,g in pairs(self.tarif_table[1]) do print(f,g[1],g[2]) end
    feed_the_dog()
    
    self.ggt = 0
    for i,coinnr in ipairs(COINVALUE) do
        self.ggt = math.floor( self:euklid(self.ggt,coinnr*100) + 0.00001 )
    end
    
    self.symbols_for_underlining = {"","","","","","","",""}
    self.symbols_for_underlining [ 1+  0*1 + 0*2 + 0*4 ] = "="
    self.symbols_for_underlining [ 1+  1*1 + 0*2 + 0*4 ] = ";"
    self.symbols_for_underlining [ 1+  0*1 + 1*2 + 0*4 ] = ","
    self.symbols_for_underlining [ 1+  1*1 + 1*2 + 0*4 ] = "o"
    self.symbols_for_underlining [ 1+  0*1 + 0*2 + 1*4 ] = "."
    self.symbols_for_underlining [ 1+  1*1 + 0*2 + 1*4 ] = ":"
    self.symbols_for_underlining [ 1+  0*1 + 1*2 + 1*4 ] = "."
    self.symbols_for_underlining [ 1+  1*1 + 1*2 + 1*4 ] = "-"

    self:init5()

end

--**********************************************************************

function App.zeitumstellung (self,pin_defs)

--    stop_timer(27)
    self.zeitumstellungs_zaehler = 0

    local naechste_zeitumstellung = 9999999999   --  in Sekunden seit 1970
    self.zeitumstellung_wert      = 0   --  die Anzahl der Stunden, die bei der Zeitumstellung addiert bzw. abgezogen wird
    
    if pin_defs == nil then
         pin_defs = self:read_pin_defs()
    end
    local x1,x2                   = string.find(pin_defs,'S%d%d%d%d%dT,')
    local summertime_mode         = 0 
    local summertime_state        = 0 -- 1: Normalzeit, 2: Sommerzeit
    self.shift_time               = 1 --  Differenz in Stunden zu GMT bei Winterzeit
    
    if x1 ~= nil then   --  der Mode, aktueller Status, bzw. die die Differenz zu GMT bei Winterzeit - in Zehntel-Stunden - koennen zusasetzlich abgespeichert werden
        summertime_mode  = tonumber(string.sub(pin_defs,x1+1,x1+1))
        summertime_state = tonumber(string.sub(pin_defs,x1+2,x1+2))
        self.shift_time  = tonumber(string.sub(pin_defs,x1+3,x1+5))
    end
    
    if summertime_mode == 0 then     --  Summertime-Mode: Der grundlegende Algorithmus. Zur Zeit ist nur Mode 1 implementiert.
        summertime_mode = 1          --                   Mode 2 bedeutet: keinerlei Umstellung
    end

    if summertime_mode == 2 then
        return
    end
    
    local akt_time = self:date("%y%m%d%H%M%S",self:time())
    
--    akt_time = "20251021005910"      --  zu Testzwecken
  
    if summertime_mode == 1 then  --   hier der allgemeine Modus fuer die europaeischen Laender
        local zaehler = 1
        local endtage = "06958169581706817069580695"
        local monate  = {"10","03"}
        local x1
        while zaehler <= #endtage do
            x1 = string.sub(endtage,zaehler,zaehler) -- Endtage bei Umstellung
            if tonumber(x1) < 2 then
                x1 = "3" .. x1
            else
                x1 = "2" .. x1
            end   --  in x1 steht nun genau die Tagesnummer der Zeitumstellung, nun noch den Monat bestimmen:
            x1 = string.format("%04u",2025+math.floor(-0.2+zaehler/2)) .. monate[1+zaehler%2] .. x1 .. "030005"
--            print("XXX1111",x1)
            if tonumber(akt_time) < tonumber(x1) then   --  x1 ist also das naechste Wechseldatum in der Zukunft
                break
            end
            zaehler = zaehler + 1
        end
--        print("wwwwww",akt_time)
        self.zeitumstellung_wert = 2*(zaehler%2) - 1   --  muss positiv sein bei Zeitumstellung nach Sommerzeit, sonst negativ
        if summertime_state == 0 then  --  State der zeit konnte nicht festegestellt werden. Nehmen wir also an, dass es sich um die aktuelle Winter-/Sommerzeit handelt
            summertime_state = 2 - zaehler%2
        end

--        print("====================================",summertime_state,zaehler,self.zeitumstellung_wert)

        if summertime_state == 2 - zaehler%2 then  --  Die Zeit ist richtig eingestellt
            self.zeitumstellungs_zaehler   = 0
            self.zeitumstellungs_intervall = math.max(100000,1000*(self:time(x1) - self:time(akt_time))) -- Zeitumstellung nach fruehestens 100 Sekunden
            print("milli",self.zeitumstellungs_intervall)
            if self.zeitumstellungs_intervall < 100000000 then --  wenn die Zeitumstellung kleiner ist als 100000 Sekunden (also etwa ein Tag), dann gelich den Zaehler direkt aktivieren
                self.zeitumstellungs_zaehler = 299   --   Zeitumstellung wird nach diesem Interval gleich aktiviert
            else  --  wenn groesser als ein Tag bis zur naechsten Zeitumstellung, dann das Intervall in 1000 abschnitte aufteilen,
                self.zeitumstellungs_intervall = math.ceil(self.zeitumstellungs_intervall / 200) -- und mit einem Timer hochzaehlen
            end
            print(27,self.zeitumstellungs_intervall/8640,self.zeitumstellungs_zaehler)
            start_timer(27,self.zeitumstellungs_intervall,0,1)
        else   --  die Zeit muss JETZT umgestellt werden
            self:zeit_umstellen()
        end
    end

    
end

--**********************************************************************

function App.zeit_umstellen (self)

    if self.ticket_wird_gerade_gedruckt == 1 then
        start_timer(15,100000,0,1)  --  spaeter nochmal versuchen
    end

    if self.anzeige_betrag:read() ~= "0,00" then
        start_timer(15,200000,0,1)  --  spaeter nochmal versuchen
    end

    local akt_time = self:time()
    akt_time       = akt_time + self.zeitumstellung_wert * 3600
    akt_time       = self:date("%y%m%d%H%M%S",akt_time)
    self.debug1:write("AAA",akt_time)
    set_date_time(string.sub(akt_time,1,4),string.sub(akt_time,5,6),string.sub(akt_time,7,8),
                  string.sub(akt_time,9,10),string.sub(akt_time,11,12),string.sub(akt_time,13,14))
    local pin_defs = self:read_pin_defs()
    local x1,x2    = string.find(pin_defs,'S%d%d%d%d%dT,')
    if x1 ~= nil then   --  falls es einen Speicherbereich gibt, den Status dadrin updaten
        local summertime_state = 1
        if self.zeitumstellung_wert > 0 then
            summertime_state    = 2
        end
        pin_defs = string.sub(pin_defs,1,x1+2) .. string.format("%1u",summertime_state) .. string.sub(pin_defs,x1+4)
        self.pin_definitions:write(pin_defs)
    end
    self:zeitumstellung(pin_defs)

end

--**********************************************************************

function App.xxzeitumstellung (self,pin_defs)

--    set_date_time(string.sub(akt_time,1,4),string.sub(akt_time,5,6),string.sub(akt_time,7,8),
--                  string.sub(akt_time,9,10),string.sub(akt_time,11,12),string.sub(akt_time,13,14))


    set_date_time("2024","10","12","11","01","22")
    
end

--**********************************************************************

function App.euklid (self,a,b)

    while b > 0 do
        local h = a % b
        a = b
        b = h
    end
    return(a)
    
end

--**********************************************************************

function App.init5 (self)

    self.last_print_date  = ""
    self.reportnumber_tmp = nil
    if start_timer ~= nil then
        start_timer(29,60000,0,1)  --  nach einer Minute den ersten heartbeat-Eintrag machen
    end
    
--    local a = xxx + 5     als Test

    
--    print("TRACEBACK",debug.traceback())
    
    self.TIMEOUT                = 18
    self.REDUCE_BACKLIGHT       = 14  --  Sekunden bis low Backlight
    self.TIMEOUTLONG            = 75
    self.TIMEOUTQRCODE          = 40.0
    self.QRCODE_REDUCE          = 32.0
    self.LOW_BACKLIGHT          = 30
    self.BLACKSCREEN            =  0
    self.WAIT_TIME_LONG_PRESS   =  3
    self.ANZAHL_PINS            =  5
    self.NO_CHECK_FOR_SIGNATURE =  1
    self.sondertarif_text       = ''
    self.reporthashes           = ","   -- hier werden die schon uebermittelten statischen Bestandteile von Reports als Hashes gemerkt
    self.static_strings         = {}
    
    SE_CLASS                    = tonumber(SERIALNUMBER) % 3800   --  Serial nummer Klasse
    local SE1                   = math.floor(1 + SE_CLASS/62)
    local SE2                   = 1 + SE_CLASS%62
    SE_CLASS                    = string.sub(CHAR64,SE1,SE1) .. string.sub(CHAR64,SE2,SE2)

    if CURRENTSHORT == nil then
        CURRENTSHORT = string.sub(CURRENT,1,1)
    end

    self.normal_test  = ""
    self.normal_test1 = ""
    self:update_pruefcode()
    
    local pd = self:read_pin_defs()
    local x1 = nil
    local x2 = nil
    x1,x2    = string.find(pd,PRINT_BF_EXIST..",")
    if x1 == nil then
        PRINT_BF_EXIST = 0
    else
        PRINT_BF_EXIST = 1
    end

--    self.shift_time          = 0
--    self.zeitumstellung_wert = 0

    self:zeitumstellung()
    
--    print(PRUEFCODE)
    
--    WAIT_26                   = 1000

    if file_open == nil then 
        self.LOW_BACKLIGHT = 70
        self.BLACKSCREEN   = 68
    end
    
    self.pin_randomseed       = Field:new(self, DEFAULT_pin_randomseed )
    
    self.pin_randomseed:add_flash_position(14*4096+72,10)
    
--    if self.simulation == 1 then
--        os.execute('mkdir flashsim 2> /dev/null')
--    end

--    self:write_flash(22,'a')
--    local h = self:read_flash(22)

    if self.simulation == 1 then
        return
    end
    
    self.who_logged_in_as_admin   = ''
    self.last_entered_pin         = ''

--  check the integrity of flash

    self.screen_switch_is_blocked = 0
    self.no_timeout               = 0
    self.admin_count              = 0
    self.control0                 = nil
    self.aktuelle_pin_eingabe     = ''
    self.long_press               = 0
    self.catch_release            = 0
    self.lfd_nr                   = tonumber(self:time()) % 100
    self.last_print_datum         = ''
    self.sondertarif_text         = ''

    self.ser_number:write(SERIALNUMBER)
    local g1, g2 = string.find(GIT_COMMIT,"%(")
    if GIT_COMMIT0 == nil then
        self.git_version:write(GIT_COMMIT)
        self.patchv:write( 'none' )
        self.git_id = bin.hextos(string.sub(GIT_COMMIT,g1+1,g1+8) .. string.sub(GIT_COMMIT,g1+1,g1+8))
    else
        local h1, h2 = string.find(GIT_COMMIT0,"%(")
        self.git_version:write(GIT_COMMIT0)
        self.patchv:write( GIT_COMMIT )
        self.git_id = bin.hextos(string.sub(GIT_COMMIT0,h1+1,h1+8) .. string.sub(GIT_COMMIT,g1+1,g1+8))
    end
    
    
    self.firmware:write(FIRMWARE_VER)
    self.screen_nr:write( get_version() )
--    self.used_pins                  = {}
    self.used_pin_groups            = {}
    self.datum_aktuelle_ticketliste = ""

    self:set_lang_nr_for_report()

    self.adm_basic:set_text(20,string.sub(self.lang_selector:read(),1,2))
    self.adm_basic:set_text(21,string.sub(self.lang_selector:read(),3,4))
    self.adm_basic:set_text(22,string.sub(self.lang_selector:read(),5,6))
    self:synchronize_buttons()
    
    self:check_if_tarif2_is_necessary()
    
    if SERIALNUMBER == "9999999" and file_open ~= nil then
        self.debug1:set_visiable(1)   --  @@  test, Aktivieren für den gruenen Debug-Balken
        self.LOW_BACKLIGHT          = 70
        self.BLACKSCREEN            = 50
    else
        self.debug1:set_visiable(0)
    end

    self.nochmal:set_visiable(-1)
    self.nochmal_button:set_visiable(-1)
    self.knob1:set_visiable(1)
    self.knob2:set_visiable(-1)
    print("HAS_KNOB",HAS_KNOB)
    if HAS_KNOB == 1 then
        self.knob1:set_visiable(-1)   --  Schaltflaeche deaktiviert, denn es gibt ja einen Knopf
    end
--    ZUFALLSWERT = 1
    self:reset_coin()
    self.qr_invalid:set_visiable(0)


--    self.debug1:write(ZUFALLSWERT)

    if RESET_FLASH > 0 then   --  mit dieswer Einstellung kann also der ganze interne Flash geloescht werden
        self:check_for_flash_reset('341674')
        self:disable_device(1)
        return
    end

--    self.uartbuffer  = {}
--    self.uartzaehler = 0
--    start_timer(7,100,0,1)
    self:switch_language(1)

    if file_open ~= nil and sd_dir == '---' then
        set_backlight(100)
        lang_nr = self.lang_nr_for_report
--        self.no_sdcard:activate("force")  --  keine SD Karte eingelegt   (Bitte SD-Karte einlegen)
--        return
    end

    self.there_is_no_sdcard = 0
    feed_the_dog()


    self:compute_shortinfo()
    if self.simulation == 0 then
        self:no_simulation()
    end

    self.report_is_integer = 1
    self.normal_test1 = "1431"
    local rep = self:read_report(1)  --  mit Check, ob vielleicht das letzte Parkticket verlorengegangen war, oder der Counter eines uebersehen hat, und Korrektur
    if rep == nil then  --  wenn eine Korrektur vorgenommen wurde, Battery Alert vorbereiten, dann hier aussteigen
        my_write_filedata1("battery.txt",sd_dir..'/'.."battery.txt","xxxxxxxxxxxxxxxx\n",over_write)
        self:disable_device()
        return
    end
    self.normal_test = "1333"
    self:write_report(rep) 
    
    rep = self:check_for_battery_alert()
    if rep == 1 then  --  wenn der Battery Alert ausgedruckt wurde: Battery Alert ausschalten, dann hier auch aussteigen, erst dann beim Neustart weiter
        my_write_filedata1("battery.txt",sd_dir..'/'.."battery.txt"," \n",over_write)  --  nochmal zur Sicherheit ueberschreiben
        self:disable_device()
        return
    end

    self.request_id = 0
    self.flash_is_not_signed = 1 - self:check_flash_signature() 
    
    self.ticketslot_start = 0
    if true and (self.flash_is_not_signed == 1 or self.report_is_integer == 0) then
        self:print('flash was not integer....')
        self.no_timeout = 1
        self:renew_timeout()

        self.ticketslot_start = 1
        lang_nr = self.lang_nr_for_report
        self.ticketslot:activate("force")
--        self.adm_proto:activate()   --  zu Testzwecken
    else
        self:update_counter(0)
        self.no_timeout         = 0
        self.machine_is_running = 1
--       set_backlight(self.BLACKSCREEN)
       
        self.standby:activate()
    end

    self.debug1:write("DISABLED" .. tostring(DISABLED)    )

--    self:check_for_nightmode()
--    print("NIGHT",self.nightmode)
    self:switch_language(1)

    start_timer(17,2000,0,1)  --  reset all, to be safe, reset_coin

    start_timer(16,2500,0,1)  --  unlock uart_sperre, manchmal kommen sonst beim Start schon Betraege

--    print("SCR---------------------------",self.standby.screen_id,self.standby,self.screens[1])

--    self.counter1:write( string.format('%7u',math.floor(self:read_counter()/100+0.5)) )
--    self.anzahl_gedruckte_parktickets:write('0')

    start_timer(14,15000,0,1)    --  Ausschalten feed the dog   watchdog


end


--****************************************************

function App.on_screen_change (self,sid)
    if self.screens ~= nil then
        self.aktueller_screen = self.screens[sid]
    end
end

--****************************************************

function App.synchronize_buttons (self,bval)

    bval = tonumber(self.tagesticket[1]:read())
    self.tagesticket[1]:set_visiable(0)
    self.tagesticket_ja[1]:set_visiable(  (bval==1) and 1 or 0 )
    self.tagesticket_nein[1]:set_visiable((bval==0) and 1 or 0 )

    bval = tonumber(self.tagesticket[2]:read())
    self.tagesticket[2]:set_visiable(0)
    self.tagesticket_ja[2]:set_visiable(  (bval==1) and 1 or 0 )
    self.tagesticket_nein[2]:set_visiable((bval==0) and 1 or 0 )

    bval = tonumber(self.pay_mode[1]:read())
    self.pay_mode[1]:set_visiable(0)
    self.pay_mode_coins[1]:set_visiable(    (bval==0) and 1 or 0 )
    self.pay_mode_qrcode[1]:set_visiable(   (bval==1) and 1 or 0 )
    self.pay_mode_both[1]:set_visiable(     (bval==2) and 1 or 0 )

    bval = tonumber(self.pay_mode[2]:read())
    self.pay_mode[2]:set_visiable(0)
    self.pay_mode_coins[2]:set_visiable(    (bval==0) and 1 or 0 )
    self.pay_mode_qrcode[2]:set_visiable(   (bval==1) and 1 or 0 )
    self.pay_mode_both[2]:set_visiable(     (bval==2) and 1 or 0 )

    bval = self.reset_device
    self.reset_device_ja:set_visiable(  (bval==1) and 1 or 0 )
    self.reset_device_nein:set_visiable((bval==0) and 1 or 0 )

end

--**********************************************************************

function App.log (self,text)

    local text1 = self:date("%y%m%d%H%M%S") .. "  " .. text .. "\n"
    my_write_filedata1("l35x.txt",sd_dir.."/".."l35x.txt",text1,add_write)
    
end

--**********************************************************************

function App.date (self,format_string,sec)  --  gives formatted date/time strings from yyyymmddhhmmss or sec

    if sec == nil then
        if file_open == nil then
            sec = os.time()
        else
            sec = App.time()   --  without parameter sec: actual time
        end
    end
    
    if format_string == nil then
        return sec
    end
    
    if sec > 1000000000000 then  --  the point in time is given as yyyymmddHHMMSS
        sec = tostring(sec)
        format_string = string.gsub(format_string,'%%y',string.sub(sec,1,4))
        format_string = string.gsub(format_string,'%%m',string.sub(sec,5,6))
        format_string = string.gsub(format_string,'%%d',string.sub(sec,7,8))
        format_string = string.gsub(format_string,'%%H',string.sub(sec,9,10))
        format_string = string.gsub(format_string,'%%M',string.sub(sec,11,12))
        format_string = string.gsub(format_string,'%%S',string.sub(sec,13,14))
        return format_string   --  and is returned due to the format string
    else
        return date1.date_format(format_string,sec)  -- returns the formatted time
    end

end


--**********************************************************************

function App.time (self,tt)  --  gives the secs of a yyyymmddHHMMSS string

    local year   = ''
    local month  = ''
    local day    = ''
    local hour   = ''
    local min    = ''
    local sec    = ''
    local week   = ''

    if tt ~= nil then
    
        year  = string.sub(tt,1,4)
        month = string.sub(tt,5,6)
        day   = string.sub(tt,7,8)
        hour  = string.sub(tt,9,10)
        min   = string.sub(tt,11,12)
        sec   = string.sub(tt,13,14)

    else
    
        if file_open == nil then
            return os.time()
        else
            year, month, day, hour, min, sec, week = get_date_time()
            print(year,month,day,hour,min,sec,week)
--        hour = hour - 12  @@  zu Testzwecken
        end
        
    end

    local sec = date1.date_reverse(tonumber(year),tonumber(month),tonumber(day),tonumber(hour),tonumber(min),tonumber(sec))
    return sec
        

end

--**********************************************************************

function App.check_for_flash_reset (self,xcode)

    print('FLASH RESET')

--    if restart_device == 0 then
--        return
--    end

    local patterns = { 341674, 627856, 524516, 788420, 634154 }
    
    if xcode ~= nil and #xcode == 0 then   --  soft reset

        print(' ... soft')
        for name,field in pairs(self.fields) do
            feed_the_dog()
            local bed = 0
            for i = 1,10,1 do
                if field == self.counter[i] then
                    bed = 1
                    break
                end
            end
            if bed == 0 then
                field:write( field.default_text )
            end
        end
        self:check_flash_signature('update')
        self.adm_soft_reset:activate()
        self:disable_device("x")
        flush_flash()                
        return
        
    end

    if xcode == nil and file_open ~= nil then

        xcode    = my_read_filedata(sd_dir..'/flash_reset.txt')
        xcode    = string.sub(xcode,1,6)
        
    end 
        
    if xcode ~= nil then   --  hard reset
            
        for i,pp in ipairs(patterns) do
            if xcode == tostring(pp) then
        
                self:disable_device("x")
                print(' ... hard')
                self.comment = "HARD"
--                for zaehler = 0,31,1 do
--                    print(zaehler)
--                    feed_the_dog()
--                    write_flash_string(1+zaehler*2048, string.rep(' ',2040))
--                end
                for zaehler = 0,511,1 do
                    print(zaehler)
                    feed_the_dog()
                    write_flash_string(1+zaehler*128, string.rep('j',123))
                    if zaehler % 20 == 0 then
                        self.comment = self.comment .. tostring(zaehler) .. " \n"
--                        self.comment = self.comment .. read_flash_string(1+zaehler*128) .. "\n"
                    end
                end
                flush_flash()                
                change_screen(13)
                return

            end
        end

    end       
       
end

--**********************************************************************

function App.xxcheck_for_nightmode (self)

    self.nightmode = ""
    local pin_defs = self:read_pin_defs()
    local x1,x2    = string.find(pin_defs,'NIGHT%d%d%d%d%d%d%d%d%d%d%sd%d')
--    x1 = 1
    if x1 ~= nil then
        self.nightmode = string.sub(pin_defs,x1+5,x1+5+11)
        self.nightmode = "180007000100"
        self.abendsec  = tonumber(string.sub(self.nightmode,1,2))*3600 + tonumber(string.sub(self.nightmode,3,4))*60 
        self.morgensec = tonumber(string.sub(self.nightmode,5,6))*3600 + tonumber(string.sub(self.nightmode,7,8))*60 
    end
    
    
end

--**********************************************************************

function App.print (self,text1,text2,text3)

    if self.debug ~= nil and file_open == nil then
        if text2 == nil then
            print(text1)
        elseif text3 == nil then
            print(text1,text2)
        else
            print(text1,text2,text3)
        end
    end

end

--**********************************************************************

function App.check_flash_signature (self,mode)


    local contents      = {}
    for i,field in ipairs(self.fields) do
        local nr = field.flash_position
        local tt = field:read()
--        print(nr,tt)
        if nr < 272 or nr > 275 or not mode == 'old_version' then   --  old version: without reportlang-field
--            print("NN",nr)
            table.insert(contents,{ nr, tt })
        end
    end
--    table.sort(contents,function (a,b) return(a[1]<b[1]) end)
    local text = 'x'

--    local rtxt1 = "--------------------------\r\n"

    for i,text1 in ipairs(contents) do
--        rtxt1 = rtxt1 .. tostring(text1[1]) .. ","..text1[2].."\r\n"
--        print("SORT",text1[1],text1[2])
        text = text .. text1[2]
    end
--    print(text)    
--    if file_open ~= nil then
--        my_write_filedata(sd_dir.."/"..CHECK_SIGN,text,over_write)
--    end
    
    local md5key = bin.stohex( md5lib.hash(text) )
    error2("flashsign01")
    local ciph = self:crypt(sec_key3,md5key)
    error2("flashsign02")
    
--    rtxt1 = rtxt1 ..ciph.."\r\n"
    
--    local printtext = my_read_filedata(sd_dir..'/check_sign.txt')
--    if printtext == nil then
--        printtext = ""
--    end
--    rtxt1 = printtext .. rtxt1
--    my_write_filedata(sd_dir..'/check_sign.txt',rtxt1,over_write)
    
    
    if mode == nil or mode == 'old_version' then

--        print("CIPH",ciph)
        for i,sign_pos in ipairs(self.signature_positions) do  --  alle moeglichen zehn Signatur-Positionen durchpruefen
            if read_flash_string(sign_pos) == ciph then
                return 1
            end
        end
        if mode == 'old_version' then
            return 0   --- nichts zu machen, weder mit neuer noch mit alter Version
        end
        return self:check_flash_signature('old_version')   --  auch noch gegen die alte Version des Flash testen

    end
    
    print('SIGN FLASH',md5key)
    local sign_idx             = string.gsub(md5key,"%D","").."1"
    --  zufaellig den naechsten Teilcounter setzen:
    self.aktueller_teilcounter = tonumber( string.sub(sign_idx,1,10) % #self.counter)
    --  zufaellig die Signatur-Position setzen:
    self.sign_idx              = string.sub(sign_idx,1,12) 
    sign_idx                   = tonumber( self.sign_idx ) % #self.signature_positions
--    print("XXWER",sign_idx)
    local sign_pos             = self.signature_positions[sign_idx+1]
--    print("SIX",sign_idx,sign_pos,ciph)
    write_flash_string(sign_pos,ciph)
    
end

--**********************************************************************

function App.compute_tan (self,slcode)

    sl = self:permutation(0,slcode)
    
    if SERIALNUMBER ~= string.sub(sl,6,12) then
        print( 'Serialnumber --> ', string.sub(sl,6,12) )
        return
    end
    
    local pin = string.sub(sl,1,5) 
    local tan = self:check_tan(0,pin)     --  zwoelfstellige TAN
--    print( 'PIN', pin, 'TAN: ', tan)

--    local ticketcode = self:permutation(1,pin..tan)
--    print( 'PIN: ', pin, tan,'TICKETCODE',ticketcode)
        
end
    

--**********************************************************************

function App.check_tan (self,mode,pin,tan)

    error2("checktan01")
    local ciph      = self:crypt(sec_key1,pin)
    error2("checktan02")
    local md5c      = bin.stohex( md5lib.hash(ciph) )
    local md5_bits  = bin.hextobin(md5c)
    
    local tan1 = 0
    for i,bit in ipairs(md5_bits) do
        tan1 = 2*tan1 + tonumber(bit)
        if i == 39 or (mode == 1 and i == 16) then
            break
        end
    end
    if mode == 1 then
        tan1 = math.ceil(10^4 + 1.29 * tan1 + 1234)   --  fuenfstellige TAN
    else
        tan1 = math.ceil(10^11 + 1.44 * tan1 + 1234)  --  zwoelfstellige TAN
    end
    print('PIN/TAN/TAN1',pin,tan,tan1)

--    print('WWE',pin,tan,tan1)

    if tan == nil then
        return tostring(tan1)
    end

    if file_open == nil or SERIALNUMBER == "9999999" then
--        print("123456",tan)
        if tan == '123412341234' or tan == '55555' then   --  @@
            return 1
        end
    end


    if tostring(tan) == tostring(tan1) then
        return 1
    else
        return 0
    end
    
end

--**********************************************************************

function App.update_pruefcode (self)


    self.is_parkticket = 1
    self.has_voucher   = 0

    PRUEFCODE        = 0
    local max_zeilen = fcheck(0,tonumber,self.max_zeilen:read())

    if max_zeilen < 0 then
        PRUEFCODE  = 10
        max_zeilen = - max_zeilen
    end
    
    PRUEFCODE  = PRUEFCODE + math.floor(max_zeilen/300+0.01)
    max_zeilen = max_zeilen % 300
    
    PRUEF_VALIDITY_SEC  = self:time(PRUEF_VALIDITY .."000000")
    QRCODE_VALIDITY_SEC = self:time(QRCODE_VALIDITY.."000000")
    
--    print("UPDATE:",PRUEFCODE)

    return(max_zeilen)
    
end

--**********************************************************************

function App.permutation (self,nr,text)

    local pm = {3,2,9,5,4,7,1,11,8,10,12,6}
    
    zaehler = 0
    text1   = {}
    while zaehler < 12 do
        zaehler =   zaehler + 1
        local i =   zaehler + (2*nr-1)
        if i    ==   0 then i = 12 end
        if i    ==  13 then i =  1 end
        i       =   pm[i]
        text1[ pm[zaehler] ] = string.sub(text, i, i)
    end
    
    local text2 = ''
    for i,x in ipairs(text1) do
        text2 = text2 .. x
    end
    
    return text          --  keine Permutation

end

--**********************************************************************

function App.request_flash_signature (self)

    local tn      = self.lang_nr_for_report
    math.randomseed(tonumber(self:time())*1000+tonumber(get_timer_value(29)))
    if self.flash_is_not_signed == 1 then
        self.request_id = math.random(93003,99981)
    elseif self.report_is_integer == 0 then
        self.request_id = math.random(86003,92981)
    end

    self.debug1:write(tostring(self.request_id) .. ' ' .. tostring(self.report_is_integer)..'  '..tostring(self:time()) )

    local sl  = tostring(self.request_id) .. trim(tostring(SERIALNUMBER))
    sl        = self:permutation(1,sl)
    sl        = 'S ' .. string.sub(sl,1,4) .. ' ' .. string.sub(sl,5,8) .. ' ' .. string.sub(sl,9,12)
--    print(sl)
    local requestinfo =
' \n' ..
' \n' ..
'QQx1d@\n' ..
'QW\n' ..
'QW\n' ..
'QQx1ba0QQx1d!QQx00\n' ..
self:ext_line( t('Request for storage signing',tn)) .. '\n' ..
self:ext_line( t('by specification of the ordernumber',tn)) .. '\n' ..
' \n' ..
'QQx1d!QQx11QQx1ba1\n' ..
sl .. "\n" ..
' \n' ..
'QQx1d!QQx00\n' ..
' \n' ..
self:ext_line( t('Details see under',tn) .. ':') .. "\n" ..
self:website() .. '/storagesign\n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
'QQx1dVQQx01\n' ..
'QW\n' ..
'QW\n' ..
'QQx1d@\n'


    local requestinfo_table = {}
    for zeile in string.gmatch(requestinfo,'[^\n]+') do
        table.insert(requestinfo_table,zeile)  --  string.sub(zeile,1,#zeile-1))
    end

    self:send_to_printer(requestinfo_table)
    
end

--**********************************************************************

function App.xxrequest_flash_signature (self)

    math.randomseed(tonumber(self:time())*1000+tonumber(get_timer_value(29)))
    if self.flash_is_not_signed == 1 then
        self.request_id = math.random(93003,99981)
    elseif self.report_is_integer == 0 then
        self.request_id = math.random(86003,92981)
    end

    self.debug1:write(tostring(self.request_id) .. ' ' .. tostring(self.report_is_integer)..'  '..tostring(self:time()) )

    local sl  = tostring(self.request_id) .. trim(tostring(SERIALNUMBER))
    sl        = self:permutation(1,sl)
    sl        = 'S ' .. string.sub(sl,1,4) .. ' ' .. string.sub(sl,5,8) .. ' ' .. string.sub(sl,9,12)
--    print(sl)
    local requestinfo = {
'',
'',
'QQx1d@',
'QW',
'QW',
'QQx1ba0QQx1d!QQx00',
t('Speichersignierung anfordern'),
t('durch Angabe der Ordernummer:'),
'',
'QQx1d!QQx11QQx1ba1',
sl,
'',    
'QQx1d!QQx00',
'',
t('Details siehe unter')..':',
t('www.12park.de/storagesign'),
'',
'',
'',
'',
'QQx1dVQQx01',
'QW',
'QW',
'QQx1d@'
}

    self:send_to_printer(requestinfo)
    
end

--**********************************************************************

function App.print_bestelldaten (self)


    local tn   = self.lang_nr_for_report
    local pin  = self.ticketslot_pin:read()
    math.randomseed(tonumber(self:time())*1000+tonumber(get_timer_value(29)))
    while #pin < 30 do
        pin = pin .. tostring(math.random(10002,85997))
    end
    self.ticketslot_pin:write(pin)

    local orderinfo =
' \n' ..
' \n' ..
'QQx1d@\n' ..
'QW\n' ..
'QW\n' ..
'QQx1ba0QQx1d!QQx00\n' ..
self:ext_line( t('Bitte Ticketslot bestellen',tn) ).. "\n" ..
self:ext_line( t('durch Ueberweisung unter',tn)   ).. "\n" ..
self:ext_line( t('Angabe einer oder mehrerer',tn) ).. "\n" ..
self:ext_line( t('Ordernummern',tn) .. ":" ) .. "\n" ..
' \n' ..
'QQx1d!QQx11QQx1ba1\n'

    while #pin > 4 do
        local sl  = string.sub(pin,1,5) .. trim(tostring(SERIALNUMBER))
        sl        = self:permutation(1,sl)
        sl        = 'T ' .. string.sub(sl,1,4) .. ' ' .. string.sub(sl,5,8) .. ' ' .. string.sub(sl,9,12)
--        print(sl)
        orderinfo = orderinfo .. sl .. "\n"
        pin       = string.sub(pin,6) 
    end    
    
    orderinfo = orderinfo ..
'QQx1d!QQx00\n' ..
self:ext_line( t('Bankverbindung siehe unter',tn) ..":") .. "\n" ..
self:website()..'/ticketslot\n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
'QQx1dVQQx01\n' ..
'QW\n' ..
'QW\n' ..
'QQx1d@\n'
    

    local orderinfo_table = {}
    for zeile in string.gmatch(orderinfo,'[^\n]+') do
        table.insert(orderinfo_table,zeile)  --  string.sub(zeile,1,#zeile-1))
    end

    self:send_to_printer(orderinfo_table)
    
end

--**********************************************************************

function App.xxprint_bestelldaten (self)


    local pin  = self.ticketslot_pin:read()
    math.randomseed(tonumber(self:time())*1000+tonumber(get_timer_value(29)))
    while #pin < 30 do
        pin = pin .. tostring(math.random(10002,85997))
    end
    self.ticketslot_pin:write(pin)

    local orderinfo = {
'',
'',
'QQx1d@',
'QW',
'QW',
'QQx1ba0QQx1d!QQx00',
t('Bitte Ticketslot bestellen'),
t('durch Ueberweisung unter'),
t('Angabe einer oder mehrerer'),
t('Ordernummern:'),
'',
'QQx1d!QQx11QQx1ba1'
}
    while #pin > 4 do
        local sl  = string.sub(pin,1,5) .. trim(tostring(SERIALNUMBER))
        sl        = self:permutation(1,sl)
        sl        = 'T ' .. string.sub(sl,1,4) .. ' ' .. string.sub(sl,5,8) .. ' ' .. string.sub(sl,9,12)
--        print(sl)
        table.insert(orderinfo,sl)
        pin       = string.sub(pin,6) 
    end    
    
    table.insert(orderinfo,'QQx1d!QQx00')
    table.insert(orderinfo,'')
    table.insert(orderinfo,t('Bankverbindung siehe unter:'))
    table.insert(orderinfo,self:website()..'/ticketslot')
    table.insert(orderinfo,'')
    table.insert(orderinfo,'')
    table.insert(orderinfo,'')
    table.insert(orderinfo,'')
    table.insert(orderinfo,'QQx1dVQQx01')
    table.insert(orderinfo,'QW')
    table.insert(orderinfo,'QW')
    table.insert(orderinfo,'QQx1d@')
    
    if file_open == nil then
        for i,zeile in ipairs(orderinfo) do
            print(zeile)
        end
    end

    self:send_to_printer(orderinfo)
    
end

--**********************************************************************

function App.website (self)

    local flag = FLAGS[self.lang_nr_for_report]
    
    if flag == "AT" then return("www.12park.at") end
    return("www.12park.de")

end

--**********************************************************************

function App.send_to_printer (self,printdata,printbuffer)

    self.print_data         = printdata
    print("SEND_TO_PRINTER",type(printdata),#printdata)
    self.delete_printbuffer = printbuffer
    start_timer(23,10,0,1)
    
end

--**********************************************************************

function App.print_testausdruck (self)


    local sum         = math.floor(self:read_counter()/100+0.5)
    local sum_format  = string.format('%7u',sum)
    local pointsalert = 'Points: ' .. sum_format .. '\n'

    if sum < 500 then
        pointsalert = pointsalert .. ' \n \n \n' .. '     PLEASE UPLOAD POINTS,   \n        CONTACT 12PARK!   \n'
    end
    
    local testausdruck1 =
' \n' ..
' \n' ..
'QQx1d@\n' ..
'QW\n' ..
'QW\n' ..
'QQx1ba0QQx1d!QQx00\n' ..
'QQx1d!QQx00\n' ..
' \n' ..
'Test             Test        \n' ..
'     Test             Test   \n' ..
'          Test             Te\n' ..
'st             Test          \n' ..
'   Test             Test     \n' ..
'        Test             Test\n' ..
'             Test            \n' ..
' Test             Test       \n' ..
'      Test             Test  \n' ..
'           Test             T\n' ..
' \n' ..
' \n' ..
pointsalert  --  .. self.comment


    local lpatch = ""
    if GIT_COMMIT0 ~= GIT_COMMIT then
        lpatch = "Patch: " .. GIT_COMMIT .. '\n'
    end

    if #self.debug3 > 10 then
    
        testausdruck1 = testausdruck1 ..
' \n' ..
' \n' ..
'QQx1d!QQx11QQx1ba1'.."ERROR REPORT".. '\n' ..
'QQx1d!QQx00QQx1ba1Please hand over to 12park\n' ..
string.format("%07u",tonumber(SERIALNUMBER)) .. " " .. GIT_COMMIT0 .. "\n" .. lpatch ..
'QQx1d!QQx00QQx1ba0\n' ..
string.sub(self.debug3,1,1500) .. "\n" ..
' \n'

    end


--    local protocolbuffer = nil
    feed_the_dog()
    local testausdruck2 = ''


    if 1==1 then

        local protocolbuffer = self:decrypt( pub_key3, my_read_filedata1('printbuffer2.txt',sd_dir..'/printbuffer2.txt') )

        if protocolbuffer ~= nil then
            testausdruck2 = testausdruck2 .. "\n" ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
'QQx1dVQQx01\n' ..    --  paper cut half
protocolbuffer ..
' \n' ..
' \n' ..
'QW\n' ..
'QW\n' ..
'QQx1d@\n'
            my_write_filedata1('printbuffer2.txt',sd_dir..'/printbuffer2.txt'," \n",over_write)
        end
        feed_the_dog()

    end
    
    local testausdruck_table = {}
    for zeile in string.gmatch(testausdruck1..testausdruck2,'[^\n]+') do
        table.insert(testausdruck_table,zeile)  --  string.sub(zeile,1,#zeile-1))
    end
    self.print_data         = testausdruck_table
    self.delete_printbuffer = 0
    start_timer(23,8000,0,1)  --  Testausdruck

end


--**********************************************************************

function App.send_to_printer1 (self,text)

    local umlaute = nil
    if LANGUAGES[lang_nr] == "DE" then
        umlaute = 1
    end
    
    local zaehler_raw = 0

    for i,zeile in ipairs(text) do
--        zeile = zeile:gsub('QQ','\\') .. '\n'
        feed_the_dog()
        if string.sub(zeile,1,9) ~= '---DEL---' then
            zeile = replace_diacritic_letters(zeile,umlaute)
            if file_open ~= nil then
                if zaehler_raw == 0 then
                    zeile = zeile .. '\r\n'
                else
                    zaehler_raw = zaehler_raw - 1
                end
                uart_send_data( self:str_to_byteArray(zeile))
            else
                print(">>",zeile)
            end
        else
            if string.sub(zeile,10) ~= '' then
                zaehler_raw = tonumber( string.sub(zeile,10) )
            end
        end
--        if i % 50 == 0 then
        if i % 9 == 0 then  -- dog neu
            feed_the_dog()
        end
    end
--        start_timer(6,1000,0,1)

end


--**********************************************************************

function App.current_screen (self)

--    return self.aktueller_screen 
  
    local screen_id = get_current_screen()
    local screen    = self.screens[screen_id]
--    print("SID",screen_id,screen)
    
    return screen
    
end

--**********************************************************************

function App.set_visiable (self,x,y,z)

    if self.simulation == 0 then
        set_visiable(x,y,z)
    else
        print('set_visiable',x,y,z)
    end

    
end

--**********************************************************************

--  Switch to the call back functions

function App.on_control_notify (self,sid,control,val)

    self:on_control_notify1(sid,control,val)
    
end

--**********************************************************************

function App.on_control_notify1 (self,sid,control,val) local s, r = xpcall(App.p_on_control_notify1,function(e) error1(e) end,self,sid,control,val) return(r) end

--**********************************************************************

function App.p_on_control_notify1 (self,sid,control,val)

    if DISABLED == 1 then
        return
    end

    local screen = self.screens[sid]

--    print('ON_CONTROL',sid,control,val)

    stop_timer(25)   --- Unterbreche die 
    stop_timer(26)   --- Ticket Report Berechnung
    self.generate_report_thread = nil
    
    if control == 95 and (screen == self.standby or screen == self.lang or screen == self.adm_device) then
--        print('XXXX',sid,screen,self.adm_device,control)
--          if self.doubleklick ~= screen then
        if val == 1 then
--            self:press_action()
--            start_timer(21,1,0,1)
--            print("WWW1",screen)
            self.doubleklick = screen
            self:on_uart_recv_data( { [0] = 0xEE, [1] = 0xB1, [2] = 21 } )
        else
--            self:release_action()
--            start_timer(22,1,0,1)
--            print("WWW2",screen)
            self.doubleklick = nil
            self:on_uart_recv_data( { [0] = 0xEE, [1] = 0xB1, [2] = 22 } )
        end
        return
    end        
        
    local long_press = self.long_press
    self.long_press  = 0
 
    if control == 95 or control == 96 or control == 4 and screen == self.coin then
        control = 80
    end
    
    if control == 94 and screen == self.lang then
        return self:press_knob(screen,val,long_press)
    end
    
    self:renew_timeout()

    if     control == 80 then
        return self:press_knob(screen,val,long_press)
    
    elseif control > 80 and control < 89 then
        return self:insert_coin(control-80+1)
    
    elseif control > 1080 and control < 1089 then
        return self:insert_coin(control-1070+1)
    
    elseif screen == self.lang and control == 7 then
        self.long_press = 1
        self:on_control_notify1(sid,80)
        
    elseif screen == self.lang and control > 0 and control < 4 then
        self:admin_bereich(control)
        self:switch_language(control)
        
    elseif screen == self.pin then
        return self:pin_eingabe(control)

    elseif screen == self.ticketslot and control == 10 then
        self:activate_ticketslot()

--  rem_projekt_vm

    elseif screen == self.ticketslot and control == 7 then
        print(1235,self.flash_is_not_signed,self.report_is_integer)
        if self.machine_is_running == 1 then
--        if self.flash_is_not_signed == 0 and self.report_is_integer == 1
--                 or self.report_is_integer == 2 then   --  ist nicht integer, sondern weggedrueckt
            self.ticketslot_start = 0
            self:print_bestelldaten()
        else
            self:request_flash_signature()   --  anfordern einer neuen Signatur durch Ausdruck
--            self:print_bestelldaten()
        end
        
    elseif screen == self.ticketslot and control == 17 then
        if file_open == nil then
            self:disable_device()
        else
            if self.machine_is_running == 1 then
--            if (self.flash_is_not_signed == 1) or not (self.report_is_integer == 0) then
--            if 0 == 0 then  --  self.ticketslot_start == 0 then
                self.adm_ext:activate()
            else
                self:disable_device()
            end
        end
        
    elseif screen == self.adm_basic and control == 99 then
        self.admin:activate()
        
    elseif screen == self.adm_basic and 29 < control and control < 33 then
        self.einzustellende_sprache = control
        self.adm_lang_sel:activate()
        
    elseif screen == self.admin and control == 12 then
        self:check_if_tarif2_is_necessary()
        self:make_tarif_table()
        self.adm_tarif:activate()
        
    elseif screen == self.admin and control == 2 then
        self.adm_basic:activate()
        
    elseif screen == self.admin and control == 13 then
        self.report_wird_gedruckt:set_visiable(0)
        self.press_protocol = 0
        self.adm_proto:activate()
        
    elseif screen == self.admin and control == 5 then
        self.adm_ext:activate()
        
    elseif screen == self.adm_proto and control == 13 then
        self.press_protocol = self.press_protocol + 1
        self:print_report()
        
    elseif screen == self.adm_ext and control == 1 then
--        self.ticketslot_pin:write( tostring(math.random(10002,89997)) )
        self.ticketslot:activate()
        
    elseif screen == self.admin and control == 99 then
        self.no_timeout             = 0
        self.who_logged_in_as_admin = ''   --  den aktuellen Benutzer abmelden
        self:compute_shortinfo()
        self:log("logged out ...")
        self.normal_test1 = "1432"
        local reports3 = self:read_report()
        self.normal_test = "1334"
error2("wr051")
        self:write_report(reports3,self:date("%y%m%d%H%M%S").."X","0,xx")
error2("wr052")
        self:zeitumstellung()
        set_backlight(self.BLACKSCREEN)
        self:update_pruefcode()
--        self:check_for_nightmode()
        self.standby:activate()
        
    elseif screen == self.adm_lang_sel then
        if control  == 99 then
            self.adm_basic:activate()
        else
            self:sprachauswahl(control)
        end
        
    elseif screen == self.adm_ext and control == 12 then
--        print("ADMIN",self.who_logged_in_as_admin)
        if self.who_logged_in_as_admin ~= '' then
            if string.sub(self.who_logged_in_as_admin,1,1) == '.' then  --  Admin-Zugriffe mit Namen, der mit Punkt beginnt,
                self.adm_ext:activate()                                 --  sind nicht erlaubt, die PIN-Liste zu
                return 'X'                                              --  lesen oder schreiben   
            end                                                         
        end
        self:pin_base(control) 
        
    elseif screen == self.adm_ext and control == 3 then
        if self.who_logged_in_as_admin ~= '' then
            if string.sub(self.who_logged_in_as_admin,1,1) == '.' then  --  Admin-Zugriffe mit Namen, der mit Punkt beginnt,
                self.adm_ext:activate()                                 --  sind nicht erlaubt, die PIN-Liste zu
                return 'X'                                              --  lesen oder schreiben   
            end                                                         --  (Code ist von oben kopiert, sorry)
        end
        self.adm_pin_new:activate()
        
    elseif screen == self.adm_ext and control == 99 then
        self.admin:activate()
        
    elseif screen == self.adm_ext and control == 8 then
        self.adm_device:activate()
        
    elseif screen == self.adm_device and control == 99 then
        self.adm_ext:activate()
        
    elseif screen == self.adm_pin_new and control == 5 then
        self.admin:activate()
      
    elseif screen == self.adm_pin_new and control == 17 then
        self:pin_list_change()
        self:pin_base(12)
        
    elseif screen == self.adm_pin_new and control == 99 then
        self.adm_ext:activate()
      
    elseif screen == self.pin_admin then
        self:pin_base(control)
        
    elseif screen == self.qrcode and (control == 2 or control == 93) then
        self:aktiviere_qr_tastenfeld(1)
    
    elseif screen == self.qrcode and control > 9 and control < 21 and val == 1 then
        self:press_qr_taste(control)
    
    elseif screen == self.qrcode and control == 94 then
        self:renew_qr_timeout()
        self.no_timeout = 0

    elseif screen == self.qrcode and control == 99 then
        stop_timer(18)
        stop_timer(3)
        self:on_timer(2)

    elseif screen == self.standby and control == 99 then
        return self:admin_bereich()

    elseif screen == self.adm_tarif and control == 20 then
        self:make_tarif_table()
        return self.adm_tarif2:activate()

    elseif screen == self.adm_tarif and control == 4 then
        return self.adm_tarif_def:activate()

    elseif screen == self.adm_tarif2 and control == 4 then
        return self.adm_tarif_def2:activate()

    elseif screen == self.adm_tarif_def and control == 99 then
        self:make_tarif_table()
        return self.adm_tarif:activate()

    elseif screen == self.adm_tarif_def2 and control == 99 then
        self:make_tarif_table()
        return self.adm_tarif2:activate()

    elseif screen == self.adm_tarif and control == 19 then
        self:make_tarif_table()
        return self.adm_tarif2:activate()


--  buttons:

    elseif screen == self.adm_tarif and control == 17 then
        local x = tonumber(self.tagesticket[1]:read())
        self.tagesticket[1]:write(tostring( (x+1)%2 ))
        print(self.tagesticket[1]:read(),1236)
        self:synchronize_buttons()

    elseif screen == self.adm_tarif2 and control == 17 then
        local x = tonumber(self.tagesticket[2]:read())
        self.tagesticket[2]:write(tostring( (x+1)%2 ))
        self:synchronize_buttons()

    elseif screen == self.adm_tarif and control == 3 then
        local x = tonumber(self.pay_mode[1]:read())
        self.pay_mode[1]:write(tostring( (x+1)%3 ))
        self:synchronize_buttons()

    elseif screen == self.adm_tarif2 and control == 3 then
        local x = tonumber(self.pay_mode[2]:read())
        self.pay_mode[2]:write(tostring( (x+1)%3 ))
        self:synchronize_buttons()

    elseif screen == self.adm_device and control == 17 then
        local x = self.reset_device
--        print("DD1",x)
        self.reset_device = (x+1)%2
--        print("DD2",self.reset_device)
        self:synchronize_buttons()

    else
        for i,field in ipairs(self.fields) do
            if field:synchronize_with_entry(screen,control) == 1 then
                break
            end
        end            

    end
        
--

end

-- cf1 = load(chunk1)   cf1()

--**********************************************************************

function App.aktiviere_qr_tastenfeld (self,mode)

    self:renew_qr_timeout()
    for nr = 1,12 do
        self.qrtaste[nr]:set_visiable(mode)
    end
        
end

--**********************************************************************

function App.press_qr_taste (self,nr)

    self:renew_qr_timeout()
    local tan_part
    if nr < 20 then
        tan_part = self.qrtan:read()
        tan_part = tan_part .. string.format("%1u",nr-10)
        self.qrtan:write(tan_part)
    elseif nr == 20 then
        tan_part = self.qrtan:read()
        tan_part = string.sub(tan_part,1,#tan_part-1)
        self.qrtan:write(tan_part)
    end
    
    if #tan_part == 5 then
--        print("TT",tan_part)
        local sbed = #self.falsche_qr_eingabe > 1 and self.falsche_qr_eingabe[1] == "15970" 
                                                  and self.falsche_qr_eingabe[2] == "15463" 

        if self:check_tan(1,tostring(self.qr_ordernr),tostring(self.qrtan:read())) == 1 or sbed then
            self.nochmal:set_visiable(-1)
            self.nochmal_button:set_visiable(-1)
            self.qr_ordernr = nil
--            self.coin:activate()
            self.no_timeout = 0
            stop_timer(18)
            self:renew_timeout()
            start_timer(7,7,0,1)
        else
            table.insert(self.falsche_qr_eingabe,self.qrtan:read())
--            self.qrtan:write("")
            if #self.falsche_qr_eingabe > 7 then
                self.start_test_nr = ""
                self:on_timer(18)
            elseif string.sub(self.qrtan:read(),1,1) == "0" then
                self.start_test_nr = self.qrtan:read()
                self:on_timer(18)
            else
                self.qr_invalid:set_visiable(1)
                start_timer(19,1700,0,1)
                self.no_timeout = 0
            end
        end
    end

end


--**********************************************************************

function App.compute_shortinfo (self)

    local pph   = self:get_price_per_hour(1)
    local ttt   = self.tagesticket[1]:read()
    local dauer = math.abs( self.max_park_hour[1]:read() )
    local d_min = string.format('%02u',math.abs( tonumber(self.max_park_minute[1]:read()))) 
    local minp  = self.min_sum[1]:read()
    
    if self.tarif_table[1] == nil then

        local text = pph .. ' ' .. CURRENT .. ' ' .. t('pro Stunde') .. ', '
        if tonumber(minp) > 0.00 then
            text = text .. t('mind.') .. ' ' .. tostring(minp):gsub('%.',',') .. ' ' .. CURRENT .. ', '
        end
        if ttt == '0' then
            text = text .. t('Höchstparkdauer') .. ' ' .. dauer .. ':' .. d_min .. ' ' .. t('Std.')
        else
            text = text .. t('Tagesticket ab')  .. ' ' .. dauer .. ' ' .. t('Std.')
        end

        self.shortinfo:write(text)
        
    else
    
        self.shortinfo:write("")
        
    end
    
end
    

--**********************************************************************
    
function App.press_knob (self,screen,val,long_press)

    local screen = self:current_screen()
--    print('RRW',screen.adm_device,screen,val,long_press)

    if false and self:current_screen() == self.standby then local f = 1  --  UART Test wenn true


    elseif screen == self.lang then
        if (long_press == 1) then
            self.pin_mode             = 'lang'
            self.ungueltig            = 0
            self.aktuelle_pin_eingabe = ''
            if self.press_release == 1 then  --  Sprache zurueckstellen bei Press und Press_long
                if self:switch_language() == 3 then
                    self:switch_language()
                end
            end
            if self.broetchen == 1 then
                self.show_text_muenzen = 1
                self:activate_sondertarif(1)
            elseif self.wohnmobil == 1 then
                self.show_text_muenzen = 1
                self:activate_sondertarif(0)
            else
                if string.find( self:read_pin_defs(),',E' ) then
                    self.pin:activate()
                end
            end
        else
            self:switch_language()
        end

    elseif screen == self.coin then
        self:insert_coin(1)

    elseif screen == self.standby then
        if long_press == 1 then
            self:admin_bereich()
        end
        if self.real_coins == 0 then
            self:reset_coin()
        end
        if self.anzeige_betrag:read() == '0,00' then
--            if self:switch_language(1) == 99199 then
--                self.coin:activate()
--            else
                self:switch_language(1)
                self:choose_flags()
                self.lang:activate()
--            end
        else
            self.coin:activate()
        end
    
    elseif screen == self.adm_device then
        if long_press == 1 then
            self:check_for_flash_reset( trim(self.reset_ok_code:read()) )
        end
    end
    
end
    
--**********************************************************************

function App.choose_flags (self)

--    print(self.lang_selector)
    local cc  = self.lang_selector:read()
    if self.flags_set ==  cc then
        return
    end
    
    local cc1 = string.sub(cc,1,2) 
    local cc2 = string.sub(cc,3,4)
    local cc3 = string.sub(cc,5,6)
    
--    print("CHHOSE_FLAGS",cc1,cc2,cc3)

    for lang,lfield in pairs(self.lang1) do
--        print(lang,lfield)
        lfield:set_visiable( (cc1==lang) and 1 or 0 ) 
    end

    for lang,lfield in pairs(self.lang2) do
        lfield:set_visiable( (cc2==lang) and 1 or 0 ) 
    end

    for lang,lfield in pairs(self.lang3) do
        lfield:set_visiable( (cc3==lang) and 1 or 0 ) 
    end

    if cc1 ~= nil then self.lang_description1:write( self.ldescriptions[ self:language_number(cc1) ] or '' ) end
    if cc2 ~= nil then self.lang_description2:write( self.ldescriptions[ self:language_number(cc2) ] or '' ) end
    if cc3 ~= nil then self.lang_description3:write( self.ldescriptions[ self:language_number(cc3) ] or '' ) end
    
end
    
--**********************************************************************

function App.admin_bereich (self,control)

--    print('11',control,self.admin_count,self.control0)

    self.press_protocol = 0
--    print("CCC",self.admin_count,self.control0,control)
    if control ~= nil and self.admin_count == 0 or self.control0 ~= nil and self.control0 ~= control then
        self.admin_count = 0
        stop_timer(4)
        self.control0 = nil
        return
    end
    self.control0 = control

    if self.admin_count == 0 then
        start_timer(4,8000,0,1)
    end

    self.admin_count = self.admin_count + 1
--    print('12',control,self.admin_count,self.control0)

    if self.admin_count > 4 then
        self.pin_mode = 'admin'
        self.admin_count = 0
        self.pin:activate()
    end
    
end

--**********************************************************************

function App.pin_eingabe (self,control)

    print('PIN'..tostring(control))
    print('AKT'..self.aktuelle_pin_eingabe)

    if self.ungueltig == nil then
        self.ungueltig = 0
    end

    
    if self.pin_wartezeit > 0 then
        return
    end
    
    if control == 14 then
        if self.pin_feld:read() == "" then
            self.no_timeout = 0
            set_backlight(self.BLACKSCREEN)
            self.standby:activate()
        else
            self.aktuelle_pin_eingabe = ''
            self.pin_feld:write('')
        end
        return
    end

    if control > 3 and control < 14 then
        self.aktuelle_pin_eingabe = self.aktuelle_pin_eingabe .. tostring((control-3)%10)
        if     #self.aktuelle_pin_eingabe == 1 then self.pin_feld:write('*')   
        elseif #self.aktuelle_pin_eingabe == 2 then self.pin_feld:write('*   *')  
        elseif #self.aktuelle_pin_eingabe == 3 then self.pin_feld:write('*   *   *') 
        elseif #self.aktuelle_pin_eingabe == 4 then self.pin_feld:write('*   *   *   *')
        elseif #self.aktuelle_pin_eingabe == 5 then self.pin_feld:write('*   *   *   *   *')
        end

        if #self.aktuelle_pin_eingabe == 5 then
            local erg = self:pruefe_pin(self.aktuelle_pin_eingabe)

--            print("RRR",self.aktuelle_pin_eingabe)
--            local u_key = tostring(self.aktuelle_pin_eingabe) .. "," .. tostring(math.ceil(tonumber(self:time())/86400))
--            print("U_KEY",u_key,self.used_pins[u_key],self.anzahl_dieselbe_pin)
--            if self.used_pins[u_key] ~= nil and self.used_pins[u_key] >= self.anzahl_dieselbe_pin then
--                erg = nil    --  ist an diesem Tag schon einmal aufgerufen worden
--            end
          
            local group_key   = tostring(self.e_gruppen_id)
            local group_datum = tostring(math.ceil(tonumber(self:time())/86400))
            if not (self.datum_aktuelle_ticketliste == group_datum) then   --  es handelt sich offenbar um einen neuen Tag, daher die Liste der verbrauchten Tickets neu anlegen
                self.used_pin_groups = {}
                self.datum_aktuelle_ticketliste = group_datum
            end
            
--            print("PPPP",self.used_pin_groups[group_key])
            
            local used_zaehler = 0
            if self.used_pin_groups[group_key] ~= nil then
                local akt_zeit_hm     = self:date('%H%M',akt_time)
                for i,pin_request in ipairs(self.used_pin_groups[group_key]) do
                    if tonumber(pin_request) > tonumber(akt_zeit_hm) then
                        used_zaehler = used_zaehler + 1
                    end
                end
            end          
            
--            print('ERG',erg,used_zaehler,self.anzahl_dieselbe_pin)
            stop_timer(5)
            self.ungueltig = self.ungueltig + 1
            if self.pin_mode == 'lang' then
                if erg == 'E' and used_zaehler < self.anzahl_dieselbe_pin then
--                    self.aktuelle_pin_eingabe = ''
                    self:activate_sondertarif(0)
                end
            elseif self.pin_mode == 'admin' then
                if erg == 'A' or erg == 'S' then
                    self.ungueltig  = 0
                    self.no_timeout = 1
                    self:log( "Admin logged in " .. self.aktuelle_pin_eingabe )
                    local aktpin = self.aktuelle_pin_eingabe
                    aktpin = string.gsub(aktpin,"1","l")
                    aktpin = string.gsub(aktpin,"2","z")
                    aktpin = string.gsub(aktpin,"3","e")
                    aktpin = string.gsub(aktpin,"4","t")
                    aktpin = string.gsub(aktpin,"5","s")
                    aktpin = string.gsub(aktpin,"6","b")
                    aktpin = string.gsub(aktpin,"7","v")
                    aktpin = string.gsub(aktpin,"8","w")
                    aktpin = string.gsub(aktpin,"9","q")
                    aktpin = string.gsub(aktpin,"0","o")
                    self.normal_test1 = "1433"
                    local reports5 = self:read_report()
                    self.normal_test = "1335"
error2("wr053")
                    self:write_report(reports5,self:date("%y%m%d%H%M%S").."X","0,"..string.sub(aktpin,1,2))
error2("wr054")
                    self.aktuelle_pin_eingabe = ''
--                    self:renew_timeout()
                    self.admin:activate()
                end
            end

            self.pin_feld:write('')

            if self.ungueltig > 0 then
                self.aktuelle_pin_eingabe = ''
                self.pin_wartezeit = 2000
                if     self.ungueltig == 1 then self.pin_wartezeit =  100
                elseif self.ungueltig == 2 then self.pin_wartezeit =  100
                elseif self.ungueltig == 3 then self.pin_wartezeit =  200
                elseif self.ungueltig == 4 then self.pin_wartezeit =  300
                elseif self.ungueltig == 5 then self.pin_wartezeit =  400
                elseif self.ungueltig == 6 then self.pin_wartezeit =  600
                elseif self.ungueltig == 7 then self.pin_wartezeit = 1000
                end
                self.pin_feld:write('***********')
                if erg == 'E' and used_zaehler >= self.anzahl_dieselbe_pin then
                    self.ungueltig_text:write(t("Nicht verfügbar"))
                else
                    self.ungueltig_text:write(t("Ungültige PIN"))
                end
                self.ungueltige_pin:set_visiable(0)
                start_timer(5,self.pin_wartezeit+1000,0,1)
            end

        end
    end

end

--**********************************************************************
                    
function App.activate_sondertarif (self,nr)

    self.ungueltig  = 0
    if self.broetchen == 0 then
        self.sondertarif_anzeige:set_visiable(0)
    end
    self.tarifnr = 2
    self.anzeige_parkdauer:write(self:date(t('ZEIT')))
    if tonumber(self.pay_mode[2]:read()) > 0 then
        self.muenzen:set_visiable(1)
        self.muenzen1:set_visiable(1)
        self.muenzen2:set_visiable(1)
        self.muenzen3:set_visiable(1)
        self.bargeldlos:set_visiable(1)
    else
        self.muenzen:set_visiable(-1)
        self.muenzen1:set_visiable(-1)
        self.muenzen2:set_visiable(-1)
        self.muenzen3:set_visiable(-1)
        self.bargeldlos:set_visiable(-1)
    end
    self:insert_coin(nr)

end

--**********************************************************************

function App.pruefe_pin (self,pin)

--    print(pin,self.superpin)
--    if pin == self.superpin then
--        return 'S'
--    end
    
--    if pin == '99987' then    @@  for test
--        return 'E'
--    end

    self.anzahl_dieselbe_pin = 99999999
    local pin_defs  = self:read_pin_defs()
    print("PIN_DEFS",pin_defs)
    local x1,x2     = string.find(pin_defs,',.'..pin..'.-,')

    if x1 ~= nil then   --  Eintrag gefunden
        local mode   = string.sub(pin_defs,x1+1,x1+1)
        local remark = string.sub(pin_defs,x1+7,x2-1)
        if trim(mode) == '' and remark ~= nil and #remark > 0 then
            mode = 'E'
        end

--  1.  erstmal die Vielfachheit der Ticketausgabe auslesen

        if mode == 'E' then
        
            self.anzahl_dieselbe_pin = 1
            local x1 = 1
            local x2 = 1
            x1,x2    = string.find(remark,"%/%d+[%+=#x]%/")
            if x1 ~= nil and x2 ~= nil then
                x2 = string.sub(remark,x1+1,x2-2)
                self.anzahl_dieselbe_pin = fcheck(1,tonumber,x2)
                if self.anzahl_dieselbe_pin == nil then
                    self.anzahl_dieselbe_pin = 1
                end
            end
            
-- 2.   jetzt die Gruppenzugehoerigkeit rauslesen 
            
            self.e_gruppen_id = pin
            x1 = 1
            x2 = 1
            x1,x2    = string.find(remark,"%/%@[^%/]+%/")
            if x1 ~= nil and x2 ~= nil then
                self.e_gruppen_id = string.sub(remark,x1+2,x2-1)
            end
            
-- 3.   Intervalle fuer Freitickets auslesen
            
            self.freiticket_interval = {"0000","2359"}
            x1 = 1
            x2 = 1
            x1,x2    = string.find(remark,"%/[0123456789,]+%/")
            if x1 ~= nil and x2 ~= nil then
                local f_int = string.sub(remark,x1+1,x2-1)
                self.freiticket_interval = {"0000"}
                local value_before       = 0
                local zeile              = ""
                for zeile in string.gmatch(f_int,'[^,]+') do
--                    print("ZZZ",zeile)
                    if #zeile == 1 then
                        zeile = "0" .. zeile + "00"
                    end
                    if #zeile == 2 then
                        zeile = zeile .. "00"
                    end
                    if #zeile == 3 then
                        zeile = "0" .. zeile
                    end
                    if #zeile == 4 then
                        if tonumber(zeile) > value_before and tonumber(zeile) < 2359 then
                            value_before = tonumber(zeile)
                            table.insert(self.freiticket_interval,zeile)
                        end
                    end
                end                    
                table.insert(self.freiticket_interval,"2359")
            end
            
-- 4.   Sondertarif_text auslesen

            self.sondertarif_text = ""
            x1 = 1
            x2 = 1
            x1,x2    = string.find(remark,"%/%.[^%/]+%/")
            if x1 ~= nil and x2 ~= nil then
                self.sondertarif_text = string.sub(remark,x1+2,x2-1)
            end                    

            if self.sondertarif_text == "" then
                x1 = 1
                x2 = 1
                x1,x2    = string.find(remark,"%/[^%@%d][^%/]+%/")
                if x1 ~= nil and x2 ~= nil then
                    self.sondertarif_text = string.sub(remark,x1+1,x2-1)
                end
            end                    

            print("PARAM",self.anzahl_dieselbe_pin,self.e_gruppen_id,self.freiticket_interval,self.sondertarif_text)


        end

        if mode == 'A' then
            self.who_logged_in_as_admin = remark
        end
        if mode == 'E' then
            self.last_entered_pin = remark
        end
        return mode

    end
  
--  Die Pruefung in der PIN-Liste hat keinen entsprechenden Eintrag gefunden, jetzt also gegen die Super-Pin pruefen  
  
    local s1       = ''   --  Superpin aus der SERIALNUMBER herstellen und dagegen pruefen
    local s2       = ''
    local sernr    = tostring(SERIALNUMBER)
    while #sernr > 0 do
        local x = string.sub(sernr,1,1)
        sernr   = string.sub(sernr,2)
        if #s1 == 0 or tonumber(x) > tonumber(string.sub(s1,#s1,#s1)) then
            s1 = s1 .. x
        else
            s2 = s2 .. x
        end
    end
    if tostring(pin) == string.sub(s1..s2,1,5) then
        x1,x2 = string.find(pin_defs,',A%d%d%d%d%d[^%.]')   --  Suche nach einem gesetzten Admin-Eintrag, der auch PIN-Zugang erlaubt
        local zufallswert = self:time() % 6  --  bei jedem 6.ten Mal durchlassen, also muss man schon oefter probieren, nicht so einfach, mit der Super-PIN einzuloggen
        print("ZUFALLSWERT:",zufallswert)
        if x1 == nil or zufallswert == 1 then    --  PIN-Verwaltung gibt, dann muss man mehrfach versuchen mit dem Standard-Passwort
            if x1 ~= nil then -- es gibt Admin-Pins, und der Zugang wurde jetzt durch einige Versuche durch die normale PIN geknackt
                while 0 == 0 do  --  daher jetzt im Nachgang alle Admin-PINs sukzessive loeschen, als Sanktion
                    x1,x2 = string.find(pin_defs,',A.-,')
                    if x1 == nil then   -- die Loesch-Arbeit ist getan, jetzt raus
                        break
                    else
                        local old_text = string.sub(pin_defs,x1,x2)  --  den gefundenen Eintrag loeschen
                        pin_defs       = string.gsub(pin_defs,old_text,',,')
                    end
                end
                self.pin_definitions:write(pin_defs)  -- die um Admin-Pins gesaeuberte Liste in den Speicher schreiben
            end
            return 'S'
        end
    end
        
    
    
end

--**********************************************************************

function App.sprachauswahl (self,control)

    local sprache = self.adm_lang_sel:get_text(control+20)

--    if string.sub(sprache,1,1) == " " then
--        return
--    end
        
    local lang_act_number = 1
    if string.sub(self.lang_selector:read(),1,2) == self.country_table[lang_nr] then
        lang_act_number = 1
    elseif string.sub(self.lang_selector:read(),3,4) == self.country_table[lang_nr] then
        lang_act_number = 2
    elseif string.sub(self.lang_selector:read(),5,6) == self.country_table[lang_nr] then
        lang_act_number = 3
    end
        

    if     self.einzustellende_sprache == 30 then
        self.setup_lang1:write(sprache)
    elseif self.einzustellende_sprache == 31 then
        self.setup_lang2:write(sprache)
    elseif self.einzustellende_sprache == 32 then
        self.setup_lang3:write(sprache)
    end
    
    local langs = self.setup_lang1:read() .. self.setup_lang2:read() .. self.setup_lang3:read()
--    print('LL',langs)
    langs = langs:gsub('-','')
    langs = langs:gsub('X','')
    langs = langs:gsub(' ','')
    if #langs == 0 then
        self.adm_basic:set_text(20,self.lang_selector:read())
    else
        if string.sub(langs,1,2) == string.sub(langs,3,4) then   --  wenn alle drei Sprachen im Auswahlmenue gleich eingestellt sind
            if string.sub(langs,1,2) == string.sub(langs,5,6) then  -- dann wird die Sprache fuer die Reports auf diese Sprache gestellt
                self.reportlang:write(string.sub(langs,1,2) )   --  (Default ist: die erste Sprache im lang_selector).
            end     --   Danach sollte man sofort wieder umstellen auf unterschiedliche Sprachen.
        end
        self.lang_selector:write(langs)
    end

    self:set_lang_nr_for_report()
    self:switch_language(lang_act_number)
    
    for sid,screen in pairs(self.screens) do
        screen.act_lang = -1
    end
        
    self.adm_basic:activate()
    
end


--**********************************************************************

function App.switch_language (self,lang)

    local langs = self.lang_selector:read()
    local anz   = #langs/2
    
--    print("LANG",lang)
    
--    if lang == 9 then return anz end

    if anz == 1 then
        self.lang = self.lang_layout1
    elseif anz == 2 then
        self.lang = self.lang_layout2
    elseif anz == 3 then
        self.lang = self.lang_layout3
    end
    
--  1.  erstmal die Sprache waehlen und highlighten

    if lang ~= nil then
--        print('new lang',control)
        set_value( self.lang.screen_id,1, 0 )
        set_value( self.lang.screen_id,2, 0 )
        set_value( self.lang.screen_id,3, 0 )
        set_value( self.lang.screen_id,lang, 1 )
    else
--        print('ANZ',anz,x)
        local x = get_value( self.lang.screen_id,anz)
        set_value( self.lang.screen_id,  3, get_value( self.lang.screen_id,2) )
        set_value( self.lang.screen_id,  2, get_value( self.lang.screen_id,1) )
        set_value( self.lang.screen_id,  1, x )
--        print (get_value(2,1), get_value(2,2), get_value(2,3), '----')
    end

-- 2. daraus den gewaehlten Laendercode herausfinden

    local actual_country = ( get_value( self.lang.screen_id,1 ) == 1 and string.sub(langs,1,2) or '' ) .. 
                           ( get_value( self.lang.screen_id,2 ) == 1 and string.sub(langs,3,4) or '' ) .. 
                           ( get_value( self.lang.screen_id,3 ) == 1 and string.sub(langs,5,6) or '' )
                  
-- 3. daraus die Sprache herausfinden

    local actual_language = 999
    for i,country in ipairs(self.country_table) do
        if self.country_table[i] == actual_country then
            actual_language = self.ldescriptions[i]
        end
    end
    

-- 4. daraus die Sprachnummer in der Translations-Tabelle herausfinden

    local nr = 1
    while nr < 1000 do
        local try_the_language = t("LANG",nr)
        if try_the_language == "" then
            break
        end
        if try_the_language == actual_language then
            lang_nr = nr
            break
        end
        nr = nr + 1
    end

--    set_language(langcode-1)
--    lang_nr = langcode + 1
    
-- 5. den aktuellen Screen gleich an die neue Sprache anpassen

    local screen1 = self:current_screen()
--    print("SC",screen1,screen1.screen_id)
    screen1:activate("force")
    
    self:compute_shortinfo()
    
    return anz 

end

--**********************************************************************

function App.language_number (self,langcode)

    local actual_lang_nr = self.cache_langcode_to_nr[langcode]
    
    if actual_lang_nr == nil then
    
        for i,lang in ipairs(self.country_table) do
            if lang == langcode then
                self.cache_langcode_to_nr[langcode] = i
                break
            end
        end

        actual_lang_nr = self.cache_langcode_to_nr[langcode]
      
    end

    return actual_lang_nr or nil

end


--**********************************************************************

function App.insert_coin (self,nr)  --  can only be entered when screen = self.coin

    if self.ticket_wird_gerade_gedruckt == 1 then
        if nr / 10 > 1 then  --  nur bei Muenzen
            if self:read_counter() > 0 then
                stop_timer(2)
                self:reset_coin()
                self.nr_coin = nr
                start_timer(28,500,0,1)
--                self:insert_coin(nr)
            end
        end
        return
    end

    self.coin:activate()
    self.nochmal:set_visiable(-1)
    self.nochmal_button:set_visiable(-1)
    self.who_logged_in_as_admin = ''

--    print("INSERT COIN INFO",self.real_coins,self.hoechstparkdauer_ist_erreicht,nr)

    
    if nr > 10 then    --  eine reale Muenze wurde eingeworfen
        nr = nr - 10
        if self.real_coins == 0 then
            self.anzeige_betrag:write('0,00')
            self.muenzen:set_visiable(-1)
            self.muenzen1:set_visiable(-1)
            self.muenzen2:set_visiable(-1)
            self.muenzen3:set_visiable(-1)
            self.bargeldlos:set_visiable(-1)
            self.real_coins = 1
        end
    end
    
--    print("NR",nr)

--    hh = "x" + 1  --  Test Fehler


    if self.real_coins == 0 and self.hoechstparkdauer_ist_erreicht == 1 and nr > 1 then
        return
    end
    

--    print( tostring(string.gsub(self.anzeige_betrag:read(),',','.' )) )

    self.amount_in_euro = tonumber( tostring(self.anzeige_betrag:read():gsub(',','.')) )
    if nr > 0 then
        self.amount_in_euro = self.amount_in_euro + COINVALUE[nr]
    end
    self.anzeige_betrag:write( string.format('%5.2f',self.amount_in_euro):gsub('%.',',') )

    self.mwst = fcheck(0,tonumber,self.tax_vat:read())
    if self.mwst == nil then
        self.mwst = 0
    end

    self.umsatzsteuer      = self.amount_in_euro * self.mwst / (100 + self.mwst)
--    print('MWST',self.tax_vat:read(),mwst,self.umsatzsteuer)

    self.akt_time          = self:time() + 35
    local parkdauer_in_sec = self:compute_parkdauer()

--    print("NNN",self.nightmode,self.morgensec,self.abendsec)
--    if self.nightmode ~= "" then
--        local abend  = 86400 * (math.floor((self.akt_time-self.morgensec+7200)/86400))
--        morgen       = (abend + 86400) + self.morgensec
--        abend        = abend           + self.abendsec
--        print("AAM",abend-1730000000,self.akt_time+parkdauer_in_sec-1730000000,morgen-1730000000)
--        if (self.akt_time + parkdauer_in_sec) > abend then
--            self.amount_in_euro = self.amount_in_euro - tonumber(string.sub(self.nightmode,9,12))*0.01 -- fiktiver Preis ohne Nachtgebuehr
--            if (self.amount_in_euro >= 0) then
--                local parkdauer_in_sec1 = self:compute_parkdauer()
--                print("TTS",self.amount_in_euro,parkdauer_in_sec1,self.akt_time+parkdauer_in_sec1-1730000000)
--                if (self.akt_time + parkdauer_in_sec1) > abend then
--                    print("MM",morgen,self.akt_time)
--                    parkdauer_in_sec = morgen - self.akt_time
--                end
--            end
--            self.amount_in_euro = self.amount_in_euro + tonumber(string.sub(self.nightmode,9,12)) -- Rueckkorrektur
--        end
--        print("PIS",parkdauer_in_sec)
--    end


    local hoechstparkdauer = tonumber(self.max_park_hour[self.tarifnr]:read()) * 3600
                           + tonumber(self.max_park_minute[self.tarifnr]:read()) * 60
    
    if hoechstparkdauer < 0 then
        self.ueberzahlung_ausweisen = 1
        hoechstparkdauer = - hoechstparkdauer
    else
        self.ueberzahlung_ausweisen = 0
    end
--    print("HH",hoechstparkdauer)

    self.parkzeitende_sec = math.ceil(self.akt_time + parkdauer_in_sec)

    local is_tagesticket = self.tagesticket[self.tarifnr]:read()
--    if tonumber(is_tagesticket) == 1 then
--        hoechstparkdauer = math.min(hoechstparkdauer,math.ceil(akt_time/86400) * 86400 
--                                  - akt_time - 40)  -- Hoechstparkzeit auf 23:59 heutiger Tag begrenzen
--    end
       
--    print(is_tagesticket,hoechstparkdauer,self.parkzeitende_sec,self.akt_time)
    self.debug1:write("HB " .. tostring(self.hoechstbetrag[1]) .. "  " .. tostring(self.hoechstbetrag[2]))

    self.hour        = -1
    self.min         = -1
--    local zusatztage = math.floor(self.parkzeitende_sec/86400) - math.floor(self.akt_time/86400)   --  kann wieder untern bleiben wg geandertem Algorithmus


    if self.parkzeitende_sec >= self.akt_time + hoechstparkdauer then
--        print("DDDDDD",self.akt_time,self.parkzeitende_sec,hoechstparkdauer,116)
        self.hoechstparkdauer_ist_erreicht = 1
        self.parkzeitende_sec = self.akt_time + hoechstparkdauer
        self.parkzeitende_hm  = self:date(t('ZEIT'),self.parkzeitende_sec)
        self.parkzeitende_day = self:date(t('DATUM'),self.parkzeitende_sec)
        parkdauer_in_sec      = hoechstparkdauer
        if tonumber(is_tagesticket) == 1 then
            self.parkzeitende_tagesticket = "2359"
            if self.tarifnr == 2 then  --  im Tarif 2 kann das "Tagesticket" in kleinere Tagesintervalle eingeteilt sein
                local akt_zeit_hm     = self:date('%H%M',self.akt_time)
                for i,f_intval in ipairs(self.freiticket_interval) do
                    if tonumber(akt_zeit_hm) < tonumber(f_intval) then
                        if tonumber(f_intval) < tonumber(self.parkzeitende_tagesticket) then
                            self.parkzeitende_tagesticket = f_intval
                        end
                    end
                end
            end
            local parkzeitende_h  = tonumber(string.sub(self.parkzeitende_tagesticket,1,2))
            local parkzeitende_m  = tonumber(string.sub(self.parkzeitende_tagesticket,3,4))
            self.parkzeitende_hm  = self:date(t('ZEIT'),parkzeitende_h*3600+parkzeitende_m*60)    --                AAAAAAAAAA
            self.parkzeitende_day = self:date(t('DATUM'),self.akt_time)  -- + 86400*zusatztage)  --  Alogorithmus geaendert: Tagesticket immer gleicher Tag
            local akt_hm          = self:date("%H%M",self.akt_time)
            self.hour             = parkzeitende_h - tonumber(string.sub(akt_hm,1,2))
            self.min              = parkzeitende_m - tonumber(string.sub(akt_hm,3,4))
--            print("TTT",self.hour,self.min,self.parkzeitende_tagesticket,akt_hm)
            if self.min < 0 then
                self.min  = self.min + 60
                self.hour = self.hour - 1
            end
            parkdauer_in_sec      = self.hour*3600 + self.min*60
--            parkdauer_in_sec = ( math.ceil(akt_time/86400) * 86400 - akt_time ) - (1440 - parkzeitende_h*60 - parkzeitende_m)*60 + 20
        end
    else
        self.parkzeitende_hm  = self:date(t('ZEIT'),self.parkzeitende_sec)
        self.parkzeitende_day = self:date(t('DATUM'),self.parkzeitende_sec)
    end

    if self.parkzeitende_sec >= self.akt_time + hoechstparkdauer - self.korrektur_hoechstparkdauer then
        self.hoechstparkdauer_ist_erreicht = 1
    end



--    print('R',parkdauer_in_sec,self.amount_in_euro)

--    local tag_ist_zu_ende = 0
--    if self:date(t('DATUM')) ~= self:date(t('DATUM'),self.park_bis_in_sec) and self.is_tagesticket == '1' then
--        self.park_bis_hm  = t('23:59')
--        self.park_bis_day = self:date(t('DATUM')) 
--        tag_ist_zu_ende = 1
--    end


--    print('WWW',park_bis_in_sec,park_bis_hm,'---------------------------')

    local zusatztage  = math.floor(self.parkzeitende_sec/86400) - math.floor(self.akt_time/86400)   --  ist nach oben gegangen wg Bem No. 7
    self.ticketanzahl = zusatztage
    self.ticketnr     = tonumber(zusatztage)
    if zusatztage == 1 then
        if tonumber(is_tagesticket) == 1 then
            zusatztage   = ""
        else
            zusatztage   = "+ 1 " .. t("Tag")
        end
        self.ticketanzahl = "1 " .. "x"
    elseif zusatztage > 1 then
        if tonumber(is_tagesticket) == 1 then
            zusatztage   = ""
        else
            zusatztage   = "+ " .. tostring(zusatztage) .. "  "  .. t("Tage")
        end
        self.ticketanzahl = tostring(self.ticketanzahl) .. " " .. "x"
    else
        zusatztage        = ""
        self.ticketanzahl = "0 " .. "x"
        self.ticketanzahl = "V V"
        self.ticketanzahl = ""   --  doch keine 0er Tickets!
    end

    if PRUEFCODE % 10 == 1 then
        self.anzeige_parkdauer:write(self.ticketanzahl)
        self.anzeige_zusatztag:write("")
    else
        self.anzeige_parkdauer:write(self.parkzeitende_hm)
        self.anzeige_zusatztag:write(zusatztage)
    end

    if self.hour == -1 then
        self.hour = math.floor((parkdauer_in_sec+30)/3600)
        self.min  = math.floor(((parkdauer_in_sec+30) - self.hour*3600)/60)
    end

--    if file_open == nil then
--        self.parkzeit:write( string.format('Parking Time: %2d:%02d',hour,min) )
--        self.header_message:write( self:date(t('DATUM'))..'\r\n'..self.park_bis_hm )
--    end

    if nr > 1 then
        self.qr_ordernr = nil
    end


    self.show_text_muenzen0 = self.show_text_muenzen
    if self.amount_in_euro >= tonumber(self.min_sum[self.tarifnr]:read()) then
        self.text1:set_visiable(1)
        self.text2:set_visiable(1)
        self.text3:set_visiable(0)
        self.text4:set_visiable(0)
        self.show_text_muenzen = 0
    else
        self.text1:set_visiable(0)
        self.text2:set_visiable(0)
        self.text3:set_visiable(1)
        self.text4:set_visiable(1)
        self.show_text_muenzen = 1
    end


    if self.coin:get_text(278) == 278 then    --  Zusatzcode, falls alte coin.tft ohne text3 und text4
        self.text1:set_visiable(1)
        self.text2:set_visiable(1)
        if self.amount_in_euro >= tonumber(self.min_sum[self.tarifnr]:read()) then
            self.text1:write("KNOPFDRUECKENA")
            self.text2:write("KNOPFDRUECKENB")
            self.show_text_muenzen = 0
        else
            self.text1:write("BITTEMUENZENA")
            self.text2:write("BITTEMUENZENB")
            self.show_text_muenzen = 1
        end
    end

    self.coin:activate(1)
    
--    print(nr,self.amount_in_euro,self.min_sum[1]:read())
    if self.hoechstparkdauer_ist_erreicht == 1 and (self.real_coins == 1 or self.amount_in_euro < 0.000001) or
              nr == 1 and self.amount_in_euro >= tonumber(self.min_sum[self.tarifnr]:read()) then

        self.last_knob_action_was_press = 0
        self.falsche_qr_eingabe = {}
        if self.real_coins == 0 and self.amount_in_euro > 0.00 then
            self.no_timeout = 1
            self:renew_qr_timeout()
            math.randomseed(tonumber(self:time())*1000+tonumber(get_timer_value(29)))
            if self.qr_ordernr == nil then
                self.qr_ordernr = math.random(10002,99987)  --  fuenfstellige zufaellige QR Ordernummer, d.h. die mit 0 beginnenden Codes
            end                                             --  koennen als Testnummern verwendet werden
            
            self:compute_html_parameter()

            local html_link = "https://pay.12park.de/pay?order="..self.transmit_nr
            print(html_link)
            self.qrcode_text:write(html_link)
            self.qrzaehler = self.qrzaehler + 1
            self.qrtan:write("")
            self.nochmal:set_visiable(1)
            self.nochmal_button:set_visiable(1)
            self:aktiviere_qr_tastenfeld(-1)
            self.qrcode:activate()
        else

            stop_timer(7)
            if self.show_text_muenzen ~= self.show_text_muenzen0 then
                self.text1:set_visiable(0)
                self.text2:set_visiable(0)
                self.text3:set_visiable(0)
                self.text4:set_visiable(0)
            end
--            self.uart_sperre = 1
            start_timer(7,7,0,1)
        end
    end
    
end

--**********************************************************************

function App.decode_order (self,order)

end


--**********************************************************************


function App.generate_tarif_3 (self)

    return(1)
    
end


--**********************************************************************

function App.reset_coin (self)

    self.anzeige_drucken:set_visiable(1)
    self.who_logged_in_as_admin        = ''
    self.last_entered_pin              = ''
    self.anzeige_betrag:write('0,00')
    local fff = self:date(t('ZEIT'))
    self.anzeige_parkdauer:write(fff)
    self.sondertarif_anzeige:set_visiable(1)
    self.sondertarif_text              = ''
    self.qrzaehler                     = 0
    self.tarifnr                       = 1
    self.ticket_wird_gerade_gedruckt   = 0
    self.real_coins                    = 0
    self.hoechstparkdauer_ist_erreicht = 0
    self.no_more_coins                 = 0
    self.nochmal:set_visiable(-1)
    self.nochmal_button:set_visiable(-1)
    self.show_text_muenzen             = 0
    self.text1:set_visiable(1)
    self.text2:set_visiable(1)
    self.aktuelle_pin_eingabe          = ''
    if self.text3 ~= nil then
        self.text3:set_visiable(0)
        self.text4:set_visiable(0)
    end
    if tonumber(self.pay_mode[1]:read()) > 0 then
        self.muenzen:set_visiable(1)
        self.muenzen1:set_visiable(1)
        self.muenzen2:set_visiable(1)
        self.muenzen3:set_visiable(1)
        self.bargeldlos:set_visiable(1)
    else
        self.muenzen:set_visiable(-1)
        self.muenzen1:set_visiable(-1)
        self.muenzen2:set_visiable(-1)
        self.muenzen3:set_visiable(-1)
        self.bargeldlos:set_visiable(-1)
    end
    
end

--**********************************************************************

function App.make_tarif_table (self)  -- Computes both tarif tables


    self.tarif_table = { {  {0,0}  }, {  {0,0}  }, { {0,0} } }
    self.broetchen   = 0
    self.wohnmobil   = 0

--    print("MAKE TARIF TABLE")

    for j = 1,2,1 do

        for i = 1,10,1 do
            local stunde = self.tarif_par[2*j-1][i]:read() 
            local preis  = self.tarif_par[2*j  ][i]:read()
            if j == 1 and tonumber(preis) == 0 then
                self.broetchen = 1
            end
            if j == 2 and tonumber(preis) == 0 then
                self.wohnmobil = 1
            end
            if i == 1 and trim(stunde) == "" then
                self.tarif_table[j] = nil
                break
            elseif trim(stunde) ~= "" and trim(preis) ~= "" then
                table.insert(self.tarif_table[j], {trim(stunde),trim(preis)} )
            end
               
        end
        
        local pph  = self.price_per_hour[j]:read()
        local pph1 = string.gsub(pph,"[()]","")
        if not (self.tarif_table[j] == nil) then
            pph1 = "(" .. pph1 .. ")"
        end
        if not (pph1 == pph) then
            self.price_per_hour[j]:write(pph1)
--            self.min_sum[j]:write(msm1)
        end

        local msm  = self.min_sum[j]:read()   --  nur fuer den Fall, dass irgendwo unerlaubterweise noch Klammern um den Mindestbetrag stehen
        local msm1 = string.gsub(msm,"[()]","")
        if not (msm1 == msm) then
            self.min_sum[j]:write(msm1)
        end
        
        
if TEST_SCENARIO == 33 then
    if j == 1 then
        self.tarif_table[j] = {  {0,2}, {24,-2}, {24,-4}, {48,-4}, {48,-6}, {72,-6},  {72,-8}, {96,-8}, {96,-10}, {120,-10}    }
    end
end
        
        if not (self.tarif_table[j] == nil) then   --   erweitere die Tariftabelle fuer den Fall, dass Mehrfachtickets eingfestellt sind
            print("TARIF TABLE")

            if (   ( not (self.tarif_table[j][10] == nil) )                          and
                   (self.tarif_table[j][8][1] == self.tarif_table[j][9][1])          and
                   (self.tarif_table[j][8][2]  < 0)                                  and
                   (self.tarif_table[j][9][2]  < 0)                                  and
                   (self.tarif_table[j][10][2] < 0)                                  and
                   (self.tarif_table[j][8][1]  % 24 == 0)                            and
                   (self.tarif_table[j][9][1]  % 24 == 0)                            and
                   (self.tarif_table[j][10][1] % 24 == 0)                            and
                   (self.tarif_table[j][10][1] - self.tarif_table[j][9][1] == 24)    and
                   (0 == 0) )  then
                local stunde              = self.tarif_table[j][10][1]
                local act_price           = self.tarif_table[j][10][2]
                local single_ticket_price = self.tarif_table[j][8][2] - self.tarif_table[j][9][2]
                while 0 == 0 do
                    act_price = act_price - single_ticket_price
                    table.insert(self.tarif_table[j],{stunde,act_price})
                    if stunde > 1000 then
                        break
                    end
                    stunde    = stunde + 24
                    table.insert(self.tarif_table[j],{stunde,act_price})
                end
            end
--            for f,g in pairs(self.tarif_table[j]) do print(f,g[1],g[2]) end
        end
                    

    end


    self.hoechstbetrag = { 0, 0 }


    for j = 1,2,1 do
        local hoechstparkdauer = math.abs( tonumber(self.max_park_hour[j]:read()) * 3600
                                         + tonumber(self.max_park_minute[j]:read()) * 60 )
--        local hoechstparkdauer = 3      

        for i = 2,10,1 do
            local stunde0 = self.tarif_par[2*j-1][i-1]:read()
            local preis0  = self.tarif_par[2*j  ][i-1]:read()
            local stunde1 = self.tarif_par[2*j-1][i]:read()
            local preis1  = self.tarif_par[2*j  ][i]:read()
--            print("A",stunde0)
            stunde0       = fcheck(0,tonumber,stunde0)
            stunde1       = fcheck(0,tonumber,stunde1)
            if (stunde0 == nil) or (stunde1 == nil) then
                break
            end
            stunde0 = math.floor(3600 * stunde0 + 0.5)
            stunde1 = math.floor(3600 * stunde1 + 0.5)
            if stunde0 <= hoechstparkdauer and hoechstparkdauer <= stunde1 and stunde0 < stunde1 then
                print(stunde0,stunde1)
                self.hoechstbetrag[j] = 0.01 * math.floor(100 * (preis0 + (preis1 - preis0) / (stunde1 - stunde0) * hoechstparkdauer) + 0.5)
                break
            end
        end
--        print("Hoechstbetrag",j,self.hoechstbetrag[j])

    end


--    Berechnung des Hoechstbetrages durch Intervallteilung
--    for j = 1,2,1 do    --  obsolet
--        self.tarifnr           = j
--        print("HP",hoechstparkdauer)
--
--        local xb =     200
--        local xa =       0
--        local xc =     nil
--        local p1 =     nil
--    
--        while (xb - xa) > 0.0001 do
--            feed_the_dog()
--            self.amount_in_euro = 0.5*(xb - xa) + xa
--            p1 = self:compute_parkdauer()
--            p1 = 0.1 *  math.floor(10 * p1 + 0.5)
--            print(xa,self.amount_in_euro,xb,p1,hoechstparkdauer)
--            if p1 < hoechstparkdauer then
--                xa = self.amount_in_euro
--            else
--                xb = self.amount_in_euro
--            end
--        end
--    
--        self.hoechstbetrag[j] = 0.01 * math.floor(100*self.amount_in_euro + 0.5) 
--        
--        print("HH",self.hoechstbetrag[j])
--        
--    end
        


end
        
--**********************************************************************

function App.compute_parkdauer (self)

    self.korrektur_hoechstparkdauer = 0
    
    if self.tarif_table[self.tarifnr] == nil then

        local pph = fcheck(0,tonumber,self:get_price_per_hour(self.tarifnr))
--        print("PRICE PER HOUR",pph)
        if pph == nil then
            pph = 0
        end
        pph = math.max(0,pph)

        local parkdauer_in_sec = 86400
        if pph > 0.000001 then
            parkdauer_in_sec = 3600 * self.amount_in_euro / pph
        end
        print("PARKDAUER",parkdauer_in_sec)
        return parkdauer_in_sec
        
    else
    
--     for f,g in pairs(self.tarif_table[self.tarifnr]) do print(f,g[1],g[2]) end

    
        local xa   = 0
        local ya   = 0
        local xb   = 0
        local yb   = 99999999
        
        self.ttd   = nil
        self.tth   = nil

        for i,entry in ipairs(self.tarif_table[self.tarifnr]) do

            local stunde  = tonumber(entry[1])
            local preis   = tonumber(entry[2])
            
            if preis < 0 then
                preis = -preis
                if stunde < 0 then
                    stunde = -stunde
                    if (self.tth == nil) then    --  cachen wg performance bei langen Tariftabellen
                        self.tth = (self.akt_time  %  3600 ) / 3600
                    end
                    stunde = stunde - self.tth       -- angefangene Minuten der angebrochenen Stunde abziehen
                    self.korrektur_hoechstparkdauer = 3600
                else
                    if (self.ttd == nil) then    --  cachen wg performance bei langen Tariftabellen
                        self.ttd = (self.akt_time  %  86400 ) / 3600
                    end
                    stunde = 0.5 + stunde - self.ttd -- angefangene Stunden und Minuten des angebrochenen Tages abziehen
                    self.korrektur_hoechstparkdauer = 86400
                end
            end
            
            if self.amount_in_euro <= tonumber(preis) then
                if preis < yb or preis == yb and stunde < xb then
                    xb   = stunde
                    yb   = preis
                    seta = 1
                end
            end
            if self.amount_in_euro >= tonumber(preis) then
                if preis > ya or preis == ya and stunde > xa then
                    xa   = stunde
                    ya   = preis
                    setb = 1
                end
            end
            if self.amount_in_euro + 0.01 < tonumber(preis) then  --  ab hier sind die Werte der Tabelle nicht mehr interessant
                break
            end 
        end

        
--        print(xa,ya,xb,yb)
        
        local diff             = math.abs(yb-ya)
        local parkdauer_in_h   = math.max(xa,xb)
        if diff > 0 then
            local verhaeltnis  = (self.amount_in_euro - ya)/diff
            parkdauer_in_h     = (xa + verhaeltnis * (xb - xa))
        end
        return(parkdauer_in_h*3600)
        
        
    end
        

end

--**********************************************************************

function App.get_price_per_hour (self,nr)

    local pph = self.price_per_hour[self.tarifnr]:read()
    pph       = fcheck(0,tonumber,pph)
    if pph == nil then
        pph = 0
    end
    
    pph = math.floor ( pph*10 ) / 10
    pph = string.format("%3.2f",pph)
    
    return pph
    
end

--**********************************************************************

function App.get_gutschein_betrag (self,nr,betrag)

    local pph1 = self.price_per_hour[self.tarifnr]:read()
    local pph  = string.gsub(pph1,"[()]","")
    pph        = fcheck(0,tonumber,pph)
    if pph == nil then
        pph = 0
    end
    
    self.voucher_percentage = (pph * 100) % 10
    if self.voucher_percentage > 8.9 then
        self.voucher_percentage = 10.0
    end

    local betrag1 = fcheck(0,tonumber,trim(string.gsub(betrag,",",".")))
    if self.ueberzahlung_ausweisen == 1 then
        betrag1 = betrag1 - self.hoechstbetrag[self.tarifnr]
    end
    if betrag1 == nil then
        return nil
    end
    if betrag1 < 0 then
        return nil
    end
--    self.ticket_betrag = betrag1
    
    betrag1 = 0.01 * math.floor( betrag1 * 10 * self.voucher_percentage + 0.5 )
    if betrag1 < 0.005 then
        return nil
    end
    
    betrag1 = string.format("%3.2f",betrag1)
    betrag1 = string.gsub(betrag1,"%.",",")
    
    return betrag1
    
end

--**********************************************************************

function App.qrcode_to_string (self,qrtable)

    local qr_string = ""
    local zaehler   = 9
    local next_byte = #qrtable
    
    for i=1,#qrtable do
        for j=1,#qrtable do
            if zaehler > 8 then
                qr_string = qr_string .. string.char(next_byte)
                next_byte = 0
                zaehler   = 1
            end
            local qrcode_cell = qrtable[i][j]
            if qrcode_cell > 0 then
                next_byte = 2*next_byte + 1
            end
            zaehler = zaehler + 1
        end
    end
    
    if zaehler < 9 then
        for i=zaehler,8 do
            next_byte = 2*next_byte
        end
        qr_string = qr_string .. string.char(next_byte)
    end
    
    return(qr_string)
    
end

--**********************************************************************

function App.sommer_winterzeit (self)


end

--**********************************************************************

function App.print_parkticket (self)   --  laeuft nur auf dem coin screen

    self.coin:activate()
    stop_timer(29)
    self.anzeige_drucken:set_visiable(0)   --  hier die Anzeige 'Ticket wird gedruckt' einblenden
    self.ticket_wird_gerade_gedruckt = 1
    self.bargeldlos:set_visiable(-1)
    local gutschein_betrag = self:get_gutschein_betrag(self.tarifnr,trim(self.anzeige_betrag:read()))
    if gutschein_betrag ~= nil then
        start_timer(2,2400,0,1)  -- Wartet, bis der Drucker fertig ist
    else 
        start_timer(2,2400,0,1)  -- Wartet, bis der Drucker fertig ist
    end
    
    print("PUNKTE:",string.format('%7u',self:read_counter()))
    
    if self.mwst > 0.0001 then
        self.mwst = (self.mwst * 1000 + 1)/100
        if math.floor(self.mwst % 10) == 0 then
            self.mwst = trim(string.format('%2u',math.floor(self.mwst/10)))
        else
            self.mwst = trim(string.format('%4.1f',self.mwst/10))
            self.mwst = self.mwst:gsub('%.',',')
        end
    end    

    local act_lang = self.country_table[lang_nr]                  --  aktuelle Sprache, vom Kunden gewaehlt
    local p_lang   = string.sub( self.lang_selector:read(),1,2 )  --  Sprache des Parkplatzes
    p_lang         = LANGUAGES[ self:language_number(p_lang) ]    --  Ummappen von Flagge zu Sprache

--    print("LANGS",lang_nr,act_lang,p_lang)
--    print("WES",self.hour,self.min)
    

    feed_the_dog()

    local x = ''
    if PRUEFCODE % 10 == 3 then
        x = self:custom_ticket()
    else   
        x = self:print_parkticket_pre()
    end
    local parkticket_text    = x[1]
    local voucher_text       = x[2]
    local sondertarif_text   = x[3]
    local print_end_data     = 1
    if #x > 3 then
        local print_end_data = x[4]
    end
    if not (self.sondertarif_text == "") then
        sondertarif_text = self.sondertarif_text
    end

    feed_the_dog()
    
    if not (act_lang == p_lang) then
        x = self:print_parkticket_pre(p_lang)
        if p_lang == "DE" then
            parkticket_text    = replace_diacritic_letters(x[1],1) .. " \n" .. parkticket_text  
            voucher_text       = replace_diacritic_letters(x[2],1) .. " \n" .. voucher_text     
            sondertarif_text   = replace_diacritic_letters(x[3],1) .. sondertarif_text 
        else
            parkticket_text    = x[1] .. " \n" .. parkticket_text  
            voucher_text       = x[2] .. " \n" .. voucher_text     
            sondertarif_text   = x[3] .. sondertarif_text 
        end
    end

    if not (tonumber(self.tarifnr) == 2) then
        sondertarif_text = "---DEL---"
    end

    feed_the_dog()

    
--    if self.parkzeitende_hm == '23:59' then
--        parkticket = t('ganzer Tag')
--    end

    local loc1           = self.location1:read()
    local loc2           = self.location2:read()
    local loc3           = self.location3:read()

    local pin_owner      = ""
    if (self.anzeige_betrag:read() == " 0,00") and (string.sub(self.last_entered_pin,1,1) == ".") then
        pin_owner = string.sub(string.gsub(self.last_entered_pin,"%A",""),1,2)
        pin_owner = string.upper(pin_owner)
    end

    feed_the_dog()


    self.lfd_nr          = (self.lfd_nr + 1) % 100

    local sign_text      = parkticket_text .. sondertarif_text .. pin_owner .. string.format("%02u",self.lfd_nr)
--    print("SIGN_TEXT",sign_text)
    sign_text            = sign_text:gsub(' ','')
    sign_text            = sign_text:gsub('\n','')
    local md5_text       = ( md5lib.hash(sign_text) )
    error2("parksign01")
    self.parkschein_sign = base27_encode( bin.hextos( self:crypt(sec_key1, md5_text) ) )
    error2("parksign02")
    self.parkschein_sign = self.parkschein_sign .. pin_owner .. string.format("%02u",self.lfd_nr)

    sign_text            = voucher_text .. pin_owner .. string.format("%02u",self.lfd_nr)
    sign_text            = sign_text:gsub(' ','')
    sign_text            = sign_text:gsub('\n','')
    local md5_text       = ( md5lib.hash(sign_text) )
    error2("vouchersign01")
    self.voucher_sign    = base27_encode( bin.hextos( self:crypt(sec_key1, md5_text) ) )
    error2("vouchersign02")
    self.voucher_sign    = self.parkschein_sign .. pin_owner .. string.format("%02u",self.lfd_nr)

    feed_the_dog()



    local sondertarif_text1 = ''
    if self.tarifnr == 2 then
        sondertarif_text  = 'QQx1d!QQx11QQx1ba1'..sondertarif_text
        sondertarif_text1 = ' \n'
    end

--    local parkzeitende_f_freiticket = ""   --  scheint obsolet zu sein

    local parkticket = 
' \n' ..
'QQx1d@\n' ..
'QW\n' ..
'QW\n' ..
'QQx1ba0QQx1d!QQx00\n' ..
parkticket_text ..
' \n'

    if PRUEFCODE % 10 == 0 then
        parkticket = parkticket ..
'QQx1d!QQx11QQx1ba1'..self.parkzeitende_day  .. '\n' ..
'QQx1d!QQx44QQx1ba1'..self.parkzeitende_hm:gsub('^0','')  .. '\n'
    else
        parkticket = parkticket ..
'QQx1d!QQx11QQx1ba1' .. t("Tickets")  .. '\n' ..
'QQx1d!QQx44QQx1ba1'..self.ticketanzahl  .. '\n'
    end

    parkticket = parkticket ..
'QQx1d!QQx00\n' ..
loc1  .. '\n' ..
loc2  .. '\n' ..
loc3  .. '\n' ..
' \n' ..
sondertarif_text  .. '\n' .. sondertarif_text1 ..
'QQx1d!QQx00' .. self.parkschein_sign  .. '\n'
--string.sub(parkschein_sign,33,64),

    local ticket_cut =
'QQx1dVQQx01\n' ..
'QW\n' ..
'QW\n' ..
'QQx1d@\n' ..
'PAUSE2002\n'

    feed_the_dog()



    local voucher    = nil
    local add_ticket = nil
    
    
    print("PRUEFCODE",PRUEFCODE)
    
    
    if PRUEFCODE > 7 then    

        
--local gg = ""    -- zu Testzwecken (Muster QR-Code)
--for i,x in ipairs(cqr) do
--    local x1 = ""
--    for j,y in ipairs(x) do
--       if y == 1 then
--            x1 = x1 .. "o"
--        else
--           x1 = x1 .. " "
--        end
--    end
--    print(x1)
--    gg = gg .. x1 .. "\n"
--end

            feed_the_dog()


    
        self:voucher_barcode()   --  Barcode oder QR-Code
        if #self.transmit_nr > 2 then

            if ( 1 == 0 ) then    --  Barcode,  ist obsolet, wir bieten QR-Code an, siehe ein paar Zeilen weiter unten

                add_ticket = 
'QQx1dwQQx02\n' ..
'QQx1dhQQx60\n' ..
'QQx1dQQx6bEQQx0C' .. self.transmit_nr .. '\n' ..
'QQx1d!QQx00 \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
'QW \n'

            else
            
                if self.model_nr > 0 then    --  das koennen nur die neuen Geraete

                    add_ticket =                                 --  QR Code from printer

'QQx1d(k30QQx31QQx41QQx32QQx00\n' ..
'QQx1d(k30QQx31QQx43QQx08\n' ..    --  groesse des qr-codes
'QQx1d(k30QQx31QQx45QQx34\n' ..    --  fehlerkorrektur
'QQx1d(kQQx09QQx01QQx31QQx50QQx30' .. self.transmit_nr .. '\n' ..
'QQx1d(k30QQx31QQx51QQx30\n' ..
--'QQx1d(kQQx04QQx00QQx31QQx41QQx31QQx00\n' ..
--'QQx1d(kQQx03QQx00QQx31QQx43QQx04\n' ..
--'QQx1d(kQQx03QQx00QQx31QQx45QQx32\n' ..
--'QQx1d(kQQx0FQQx00QQx31QQx50QQx30' .. self.transmit_nr .. '\n' ..
--'QQx1ba1QQx1d(kQQx03QQx00QQx31QQx51QQx30\n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
'QW \n'
                else
                    feed_the_dog()
            add_ticket = self:parkticket_qrcode(self.transmit_nr) .. "\n" ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
'QQx1bQQx331\n' ..
'QW \n'

                feed_the_dog()
            end

            end

        end
    end
    
    if custom_voucher then
        voucher = self:custom_voucher()
    end
            feed_the_dog()
    if voucher == nil then
        if gutschein_betrag ~= nil then
            
            voucher =
' \n' ..
' \n' ..
'QQx1d@\n' ..
'QW\n' ..
'QW\n' ..
'QQx1ba0QQx1d!QQx00\n' ..
voucher_text ..
' \n' ..
'QQx1d!QQx11QQx1ba1'..self.parkzeitende_day  .. '\n' ..
' \n' ..
'QQx1d!QQx11QQx1ba1'..gutschein_betrag:gsub('%.',',') .. " " .. CURRENT  .. '\n' ..
'QQx1d!QQx00\n' ..
loc1  .. ' \n' ..
loc2  .. ' \n' ..
loc3  .. ' \n' ..
' \n' ..
'QQx1d!QQx00\n' ..
self.voucher_sign  .. '\n' ..
--string.sub(parkschein_sign,33,64),
' \n' ..
' \n' ..
' \n' ..
'QQx1dVQQx01\n' ..
'QW\n' ..
'QW\n' ..
'QQx1d@\n'

        end

    end

            feed_the_dog()
    
    if add_ticket == nil then
        add_ticket = ' \n' .. ' \n' .. ' \n' .. ' \n' .. ' \n' .. ' \n'
    end
    parkticket = parkticket .. add_ticket .. ticket_cut
    if voucher ~= nil then
        parkticket = parkticket .. voucher
    end


    local parkticket_table = {}
    for zeile in string.gmatch(parkticket,'[^\n]+') do
        table.insert(parkticket_table,zeile)  --  string.sub(zeile,1,#zeile-1))
    end

  

            feed_the_dog()


    if self.tarifnr == 2 then  --  das ist immer mit PIN-Eingabe
--        print("PPP-------------------------------------PPP",self.aktuelle_pin_eingabe)
--        local u_key = tostring(self.aktuelle_pin_eingabe) .. "," .. tostring(math.ceil(tonumber(self:time())/86400))
--        print("PP_KEY",u_key)
--        if self.used_pins[u_key] == nil then
--            self.used_pins[u_key] = 1
--       else
--            self.used_pins[u_key] = self.used_pins[u_key] + 1
--        end
--        self.aktuelle_pin_eingabe = ''

        local group_key   = tostring(self.e_gruppen_id)
--        local group_datum = tostring(math.ceil(tonumber(self:time())/86400))
--        if self.datum_aktuelle_ticketliste == group_datum then   --  es handelt sich offenbar um einen neuen Tag, daher die Liste der verbrauchten Tickets neu anlegen
--            self.used_pin_groups = {}
--            self.datum_aktuelle_ticketliste = group_datum
--        end
            
        if self.used_pin_groups[group_key] == nil then
            self.used_pin_groups[group_key] = {}
        end
        table.insert(self.used_pin_groups[group_key],self.parkzeitende_tagesticket)
--        self.aktuelle_pin_eingabe = ''

    end


            feed_the_dog()


    if #self.falsche_qr_eingabe > 1 and self.falsche_qr_eingabe[1] == "15970" and self.falsche_qr_eingabe[2] == "15463" then
        self.falsche_qr_eingabe = {}
        start_timer(29,20000,0,1)
    else
        self.normal_test1 = "1434"
        local reports = self:read_report()  -- die Daten in das Ticketreporting hineinschreiben
        self:update_counter( -100*math.max(1, math.floor(0.99999+self.amount_in_euro*POINTS_PER_EURO) ) )
        local current = "Q"
        if self.real_coins > 0 then
            current = CURRENTSHORT
        end
--        self:write_report(reports,self:date("%y%m%d%H%M%S").."B",self.anzeige_betrag:read())
        local anz_betr = self.anzeige_betrag:read()  --  wenn der Betrag 0,00, wie bei einem Freiticket, dann die ersten Buchstaben aus der vorher gewaehlten PIN-Eingabe
        if anz_betr == " 0,00" then                  --  statt der Nachkommastellen bringen, um anzuzeigen, von welcher PIN das kam.
            local betrag_marker = string.gsub(self.last_entered_pin,"%A","")
            if #betrag_marker == 1 then
                betrag_marker = betrag_marker .. "X"
            end
            if #betrag_marker > 1 then
                anz_betr = "0," .. string.sub(betrag_marker,1,2)
            end
            self.last_entered_pin = ""
        end
        self.normal_test = "1336"
error2("wr055")
        self:write_report(reports,self:date("%y%m%d%H%M%S")..current,anz_betr)
error2("wr056")
        start_timer(29,3300000,0,1)
    end
    
    WAIT_26          = 400               --  Intervall, in dem auf den naechsten Block der Signatur-Berechnung
    self.squeeze_out = nil               --  gewartet wird. Nur volle Ticketreports werden gedruckt.
    start_timer(25,10000,0,1)            --  Starten der Berechnung. Diese wird abgebrochen, wenn irgendeine Aktion am Automaten erfolgt.
                                         --  Das ist aber nicht schlimm, beim naechsten Ticketdruck wird ja nochmal angefangen. Bis es durchgegangen ist.

    local sum = tonumber( self.anzahl_gedruckte_parktickets:read() )
    self.anzahl_gedruckte_parktickets:write( string.format("%1u",sum+1) )
    self.sondertarif_text = ''   --  wahrscheinlich nicht notwendig hier, wird ja in reset_coin zurueckgesetzt
    self:send_to_printer(parkticket_table)

            feed_the_dog()


    

--  Parkticket verschluesselt abspeichern

    local reportfile  = self.parkschein_sign --  .. '_' .. 'parkticket_' .. SERIALNUMBER .. '_' .. self:date('%y%m%d') .. "_" .. self:date('%H%M%S') .. ".txt"
    self.jahresordner = self:date('%y')
    self:store_encrypted_file(string.gsub(parkticket,"---DEL---\n",""),md5lib.hash(reportfile))
 

    if (self.NO_CHECK_FOR_SIGNATURE == 0) and (self.flash_is_not_signed == 0) and (self.report_is_integer == 1) then  --  fuer Debugging des Signatur-Fehlers
        self.normal_test = "1337"
error2("wr60")
        self:write_report(reports,self:date("%y%m%d%H%M%S")..current,anz_betr)
error2("wr61")
        if (1 - self:check_flash_signature()) == 1 then
            self.normal_test = "1338"
error2("wr62")
            self:write_report(reports,self:date("%y%m%d%H%M%S").."X","0,06")
error2("wr63")
            self.NO_CHECK_FOR_SIGNATURE = 1
        end
        if self.report_is_integer0 ~= self.report_is_integer then
            self.normal_test = "1339"
error2("wr64")
            self:write_report(reports,self:date("%y%m%d%H%M%S").."X","0,05")
error2("wr65")
            self.NO_CHECK_FOR_SIGNATURE = 1
        end
    end    


            feed_the_dog()





--  Verschluesselung Ende

    
    if file_open == nil then
--        for i,zeile in ipairs(parkticket) do
--            print(zeile)
--        end
        print("RESTLICHE PUNKTE (x 100):",self:read_counter())
    end
        
end

--**********************************************************************

function App.parkticket_qrcode (self,text)

    CALCULATE_QRCODE_FOR_REPORT = 0
    local dd, cqr = qrc.qrcode(text)
    CALCULATE_QRCODE_FOR_REPORT = 0
    feed_the_dog()
    local erg, dotres = self:qr_to_bitmap(6,cqr)
    feed_the_dog()
    
    local padding = 'QQx1b$QQx70QQx00' --  .. string.format("%02x",(384 - #cqr*6)/2)
    local text1   = 'QQx1bQQx330QQx1bQQx61QQx48\n'
--    padding = ""
    for i=1,#erg do
        feed_the_dog()
        local laenge = #erg[i]/lensym
        local nr1    = string.format("%02x",laenge % 256)
        local nr2    = string.format("%02x",math.floor(laenge/256))
        text1        = text1  .. padding .. 'QQx1b*QQx' .. dotres .. "QQx" .. nr1 .. "QQx" .. nr2 .. erg[i] .. "\n"
    end
    return(text1)

end

--**********************************************************************

function App.print_parkticket_pre (self,langcode)


    local tn = nil
    print("TN0",langcode,tn)



    if not (langcode == nil) then
        tn = self:language_number(langcode)
    end

    print("TN",langcode,tn)

    local parkticket_dauer = string.format("%1u",self.hour)..' '..t('Std.',tn)..' '..string.format("%1u",self.min)..' '..t('Min.',tn)


--    if self.parkzeitende_hm == '23:59' then
--        parkticket = t('ganzer Tag')
--    end

    local ust = '---DEL---'
    if self.umsatzsteuer > 0.001 then
        ust = self:ext_line( t('davon',tn) .. ' ' .. self.mwst .. '% ' .. t('Umsatzsteuer',tn)..':xXxXxXxXx'..trim(string.format('%5.2f',self.umsatzsteuer):gsub('%.',','))..' '..CURRENT  )
    end
    


    local bargeld = t('Barzahlung',tn)
    if self.real_coins == 0 then
        bargeld = t('bargeldlos',tn)
    end
    
    local datum1         = self:date(t('DATUM',tn),self.akt_time)
    local ticketvom      = self:ext_line(  t('Ticket vom',tn)..'xXxXxXxXx'..datum1..' '..self:date(t('ZEIT',tn),self.akt_time):gsub('^0','')  )
    local parkscheinvom  = self:ext_line(  t('Parkschein vom',tn)..'xXxXxXxXx'..datum1..' '..self:date(t('ZEIT',tn),self.akt_time):gsub('^0','')  )

    local parkpreis      = trim(self.anzeige_betrag:read())
--        print("PP0",parkpreis)
    if self.ueberzahlung_ausweisen == 1 then
        local parkpreis1 = string.gsub(parkpreis,',','.')
        parkpreis1       = fcheck(0,tonumber,parkpreis1)
--        print("PP",parkpreis1)
        if (parkpreis1 ~= nil) and (tonumber(parkpreis1) > 0) then
            parkpreis         = 0.01 * math.floor(100*(tonumber(parkpreis1) - self.hoechstbetrag[self.tarifnr])+0.5)
            self.umsatzsteuer = parkpreis * self.mwst / (100 + self.mwst)
            parkpreis         = string.format("%3.2f",parkpreis)
            parkpreis         = string.gsub(parkpreis,'%.',',')
        end
        if self.umsatzsteuer > 0.001 then
            ust = self:ext_line( t('davon',tn) .. ' ' .. self.mwst .. '% ' .. t('Umsatzsteuer',tn)..':xXxXxXxXx'..trim(string.format('%5.2f',self.umsatzsteuer):gsub('%.',','))..' '..CURRENT  )
        end
    end



    parkpreis            = self:ext_line(  t('Preis',tn)..': '..parkpreis..' '..CURRENT..'xXxXxXxXx'..bargeld   )
    local gutscheinvom   = self:ext_line(  t('Gutschein vom',tn)..' '..datum1..'xXxXxXxXx'..self:date(t('ZEIT',tn),self.akt_time):gsub('^0','')   )
    local gueltig_bis    = t('Gueltig bis',tn)..': '

    if PRUEFCODE % 10 == 0 then

        local park_dauer     = self:ext_line(  t('Parkdauer',tn)..':'..'xXxXxXxXx'..parkticket_dauer  )
        local parken_bis     = t('Parken bis',tn)..': '

        local parkticket =
parkscheinvom .. '\n' ..
parkpreis  .. '\n' ..
ust  .. '\n' ..
park_dauer  .. '\n' ..
parken_bis  .. '\n'

    local voucher =
gutscheinvom .. '\n' ..
gueltig_bis  .. '\n'

        local sondertarif = t("Sondertarif",tn)
    
        return({parkticket,voucher,sondertarif})
        
    end


    if PRUEFCODE % 10 == 1 then

        local parkticket =
ticketvom .. '\n' ..
parkpreis  .. '\n' ..
ust  .. '\n'

        local voucher =
gutscheinvom .. '\n' ..
gueltig_bis  .. '\n'

        local sondertarif = t("Sondertarif",tn)

        return({parkticket,voucher,sondertarif})

    end

    
    if PRUEFCODE % 10 == 2 then

        return({"TICKET",'',''})

    end
    
end

--*********************************************************************


function App.compute_html_parameter (self)

    self:voucher_barcode(1)

end

--*********************************************************************

function App.voucher_barcode (self,is_qrcode)

--    print("PARS",self.akt_time,self.ticketnr,self.amount_in_euro)

    local gmt_time          = self.akt_time - 360*self.shift_time
    if self.zeitumstellung_wert < 0 then
        gmt_time = gmt_time + self.zeitumstellung_wert  --  die naechste Zeitumstellung ist zu Winterzeit, es ist also Sommerzeit, daher diesen
    end                                                 --  zusaetzlichen negativen Wert hier abziehen
    
    local modulo_time       = gmt_time  % (32768*32768/8)
    local modulo_price      = math.floor(self.amount_in_euro*100+0.001) % 32768
    
    local modulo_anzahl     = self.ticketnr % 8
    
    local time_anzahl       = modulo_anzahl * (32768*32768/8) + modulo_time
    
    local time_price        = time_anzahl * 32768 + modulo_price  -- wg fuehrender Nullen
    print(time_price,self.ticketnr,modulo_anzahl,time_anzahl)
  
    self.transmit_nr        = ""
    
    local drei              = 1
    
    local anz_pruefcodes    = math.floor(#VOUCHER_SIGNA_TABLE/drei) - VOUCHER_M

    local lookup_line       = time_price % anz_pruefcodes
    print (lookup_line,"  LOOKUP_LINE  ---------------")
    
    local serno_ordernr     = ''

    if (is_qrcode == 1) then
        if (tonumber(self.akt_time) > tonumber(QRCODE_VALIDITY_SEC)) then
            return
        end
        local modulo_serno = SERIALNUMBER % 32768
        serno_ordernr      = modulo_serno * 32768 + (self.qr_ordernr % 32768)
        lookup_line        = (lookup_line + serno_ordernr % anz_pruefcodes) % anz_pruefcodes
        serno_ordernr      = string.format("%1x",serno_ordernr)
    else
        if (tonumber(self.akt_time) > tonumber(PRUEF_VALIDITY_SEC)) then
            return
        end
    end
        
    local voucher_signature = string.sub(VOUCHER_SIGNA_TABLE,drei*lookup_line+1,drei*lookup_line+3)
    
    local transmit_nr_hex   = string.format("%1x",32768*32768*32768+time_price)..serno_ordernr
--    print(time_price,serno_ordernr)

    local transmit_nr_s     = bin.hextos( transmit_nr_hex )
    local transmit_nr       = base32.encode( transmit_nr_s )

    self.transmit_nr        = string.sub(transmit_nr,2) .. voucher_signature
--    print(self.transmit_nr)

    
end

--******************************************************************

function App.xxvoucher_barcode (self,is_qrcode)

    self.akt_time = 1723651252
    self.amount_in_euro     = 2
    local modulo_time       = self.akt_time  % (326768*32768*4)
    local modulo_price      = math.floor(self.amount_in_euro*100+0.01) % 32768
    print(modulo_time,modulo_price)
    
    modulo_anzahl           = self.ticketnr % 8
    
    local time_anzahl       = 32768*32768 + modulo_anzahl * (32768*32768/8) + modulo_time
    
    local time_price        = time_anzahl * 32768 + modulo_price
    print("ALT",time_price,self.ticketnr,modulo_anzahl,time_anzahl)
    
    self.transmit_nr        = ""
    
    local anz_pruefcodes    = math.floor(#VOUCHER_SIGNA_TABLE/3) - VOUCHER_M

    local lookup_line       = time_price % anz_pruefcodes

    local serno_ordernr     = ''
    
    if (is_qrcode == 1) then
        if (self.akt_time > QRCODE_VALIDITY_SEC) then
            return
        end
        local modulo_serno = SERIALNUMBER % 32768
        serno_ordernr      = modulo_serno * 32768 + (self.qr_ordernr % 32768)
        lookup_line        = (lookup_line + serno_ordernr % anz_pruefcodes) % anz_pruefcodes
        serno_ordernr      = string.format("%1x",serno_ordernr)
    else
        print("CHECK",self.akt_time,PRUEF_VALIDITY_SEC)
        if (self.akt_time > PRUEF_VALIDITY_SEC) then
            return
        end
    end
        
    local voucher_signature = string.sub(VOUCHER_SIGNA_TABLE,3*lookup_line+1,3*lookup_line+3)
    
    local transmit_nr_hex   = string.format("%1x",time_price)..serno_ordernr

    local transmit_nr_s     = bin.hextos( transmit_nr_hex )
    local transmit_nr       = base32.encode( transmit_nr_s )
    print("TRANSMIT",transmit_nr,lookup_line,voucher_signature)

    self.transmit_nr        = string.sub(transmit_nr,2) .. voucher_signature

    
end


--************************************************************************


function App.voucher_barcode_reverse (self)

    local a = string.sub(self.transmit_nr,1,1)
    local b = string.sub(self.transmit_nr,2,6)
    local c = string.sub(self.transmit_nr,7,9)
    local d = string.sub(self.transmit_nr,10,12)
    
    b_time1 = self.akt_time % (32768*32*32)
    b_time2 = base32.decode( b )
    b_time2 = bin.stohex( b_time2 )
    b_time2 = tonumber("0x" .. b_time2)

    print(b,b_time1,b_time9,b_time2)
    
    print( base32.encode ( bin.hextos( string.format("%1x",self.ticketnr) ) ) )
    

end

--************************************************************************

function App.custom_voucher (self)

    local voucher     = nil
    return voucher
    
end


--**********************************************************************

function App.ext_line (self,zeile)   --  make_two_lines_out_of_one_if_too_long

    local zeile1 = replace_diacritic_letters(zeile)
    if #zeile1 > PAPIERBREITE + 8 then
        zeile = string.gsub(zeile,"xXxXxXxXx","\n")
        
        for z1 in string.gmatch(zeile,'[^\n]+') do
            local z2 = replace_diacritic_letters(z1)
            if #z2 > PAPIERBREITE then
    
                zeile = string.gsub(zeile,"\n"," ")
                local new_zeile1   = ""
                local new_zeile2   = ""
                local new_zeile_tr = ""
                for z3 in string.gmatch(zeile,'[^ ]+') do
                    print(z3)
                    local z3_red = replace_diacritic_letters(z3)
                    if new_zeile2 == "" then
                        if #new_zeile_tr + #z3_red + 1 > PAPIERBREITE then
                            new_zeile2 = new_zeile2 .. z3
                        elseif new_zeile1 == "" then
                            new_zeile1   = new_zeile1   .. z3
                            new_zeile_tr = new_zeile_tr .. z3_red
                        else
                            new_zeile1   = new_zeile1   .. " " .. z3
                            new_zeile_tr = new_zeile_tr .. " " .. z3_red
                        end
                    else
                        new_zeile2 = new_zeile2 .. " " .. z3
                    end
                end
                zeile = new_zeile1 .. "\n" .. new_zeile2
--                print("223344 zeile",zeile)
                
            end
            break
        end

    else
        zeile = string.gsub(zeile,"xXxXxXxXx"," ")
    end
    
    return(zeile)
    
end

--**********************************************************************

function App.store_encrypted_file (self,filetxt,reportfile,reportfile1)
    
    if reportfile1 ~= nil then
        my_write_filedata1(reportfile1,sd_dir..'/'..reportfile1,filetxt,over_write)
    end

    error2("filesign01")
    filetxt    = self:crypt(sec_key3,filetxt,80)
    error2("filesign02")
    reportfile = string.gsub(self:crypt(sec_key3,reportfile,9999),"[\n ]","") .. ".enc"
    error2("filesign03")
--    print("WWWWW 223344",self.sign_idx)
    if #XFILES > 0 then
        reportfile = XFILES[tonumber(self.sign_idx)%#XFILES+1] .. "/" .. reportfile
    else
        for i,jahr in ipairs(sd_subdirs) do    --  herausfinden, ob es ein Jahres-Verzeichnis gibt
            if jahr == self.jahresordner then
                reportfile = jahr .. '/' .. reportfile
            end
        end
    end
    error2("filesign04 "..reportfile)
    my_write_filedata1(reportfile,sd_dir..'/'..reportfile,filetxt,over_write)
    error2("filesign05")

    return(reportfile)

end

--**********************************************************************

function create_signa_table (anzahl,linesize)  --  for signature lookup table


    local rseed = string.sub(sec_key,4,10)  --  choose specific randomseed, depending on serialnumber
--    print("RSEED",rseed)
    rseed       = bin.hextos(rseed)
    rseed       = base10.encode(rseed)
--    print("RSEED1",rseed)
    math.randomseed(tonumber(rseed))

    local max_anzahl  = math.floor(tonumber(anzahl) / 3) * 3
    local zaehler     = 0
--    local ctable      = "0123456789ABCDEFGHJKLMNPQRSTWXYZ"
    local ctable      = "RY1T9W2XF0GZH4JKSA67B3CDEL5M8QPN"

    local text        = ""

    while (zaehler < max_anzahl) do  -- hier wird eine neue Pruefcodetabelle geschrieben
        
        rr      = math.random(1,32)
        rr1     = string.sub(ctable,rr,rr)
        zaehler = zaehler + 1
        text    = text .. rr1
        if zaehler % linesize == 0 then
            text = text .. "\n"
        end
        
    end
--    print(text)
    
    local text1 = string.gsub(text, '%s+', '')
--    print (#VOUCHER_SIGNA_TABLE)
--    print (#text1)

    
    if not (string.gsub(text, '%s+', '') == VOUCHER_SIGNA_TABLE) then  --  wenn die neu erzeugte Pruefcodetabelle nicht mit der internen uebereinstimmt
        text = string.sub(text,1,#text-30) .. "    " ..  string.sub(text,#text-29,#text) -- dann wird in der letzten Zeile eine Luecke mit ausgegeben
    end
--    print(text1)
    
    return(text)
    
end


--**********************************************************************

function fcheck (error_val,func,arg1,arg2,arg3,arg4)

    function fcheck1 (func,arg1,arg2,arg3,arg4) fcheck_erg = func(arg1,arg2,arg3,arg4) end

    if pcall(fcheck1,func,arg1,arg2,arg3,arg4) then
        if fcheck_erg == nil then
            return error_val
        else
            return fcheck_erg
        end
    else
        return error_val
    end

end

--**********************************************************************

function App.update_counter (self,betrag)

    local sum = 0
    
    if betrag > 0 then  --  Beim Aufladen der Einzel-Counter denjenigen suchen, der am kleinsten ist
    
        local count0 = nil
        local gesamt = 0
        
        for i,count in ipairs(self.counter) do
            count_amount = tonumber(count:read())
            gesamt       = gesamt + count_amount   --  ermitteln, ob der Gesamtzaehler vielleicht negativ war
            if count0 == nil or count_amount < tonumber(count0:read()) then
                count0 = count
            end
        end
        sum = tonumber( count0:read() )
        if gesamt < 0 then
            betrag = betrag - gesamt  --  negativen Gesamtsaldo ausgleichen
        end 
        
        count0:write( string.format("%1.0f",sum+betrag) ) --  Teilzaehler erhoehen
        
    elseif betrag < 0 then  --  beim Abziehen von Punkten einen zufaelligen Teil-Counter waehlen

        self.aktueller_teilcounter = (self.aktueller_teilcounter + 1) % #self.counter
        sum = tonumber( self.counter[ self.aktueller_teilcounter+1 ]:read() )
        self.counter[ self.aktueller_teilcounter+1 ]:write( string.format("%1.0f",sum+betrag) )

    end        

    sum = self:read_counter()
    self.counter1:write(string.format('%7u',math.floor(sum/100+0.5)))
    
end

--**********************************************************************

function App.read_counter (self,raw)

    local sum = 0
    for i,count in ipairs(self.counter) do
--        if i < 200 then
--            tonumber(count:read())
--        end
        sum = sum + tonumber(count:read())
    end
    
    return math.max(0,sum)
    
end

--**********************************************************************

function App.str_to_byteArray(self,str)

local i=1
local k=0
local bytes={}
while i<=#str do 
    if string.sub(str,i,i+2)=='QQx' or string.sub(str,i,i+2)=='WxxxxxxxxxxxWx' then
        bytes[k]=tonumber('0x'..string.sub(str,i+3,i+4))
        i=i+5
    elseif string.sub(str,i,i+1)=='QW' then
        bytes={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
                bytes[0]=0 --'QW' erzeugt table mit 64 Nullbytes, start bei index 0, zwei Zeilen mit 'QW' zum Leeren des Buffers
        break
    else 
        bytes[k]=string.byte(str,i)
        i=i+1
    end
    k=k+1
end
return bytes 

end

--**********************************************************************

function App.read_report (self,rp)

    local reports     = {}
    local text        = 'x'
    local signature   = ''
    local checknr     = ''
    local text1       = ''
    local reconstruct = rp
    
    self.report_is_integer0 = self.report_is_integer
    
    local reportnr = fcheck(0,tonumber,self.reportnumber:read())
    if reportnr == nil then
        reportnr = 0
    end

    local akt_count = self:read_counter()

--    change_screen(19)
--    if true then return end

    local text  = self:decrypt( pub_key3, my_read_filedata1('newreport.txt',sd_dir..'/newreport.txt') )
    if file_open == nil then
        if self.alt_report ~= nil then
            print("ALT",self.alt_report)
            text = self.alt_report
        end
    end

    local text_normal = normal_signs(text)
    if text_normal ~= '' then
        error1("XA "..self.normal_test1.. " "..string.sub(text,1,100))
        text = ''
    end

--    print("223344",text)
    for zeile in string.gmatch(text,'[^\n]+') do
        if trim(zeile) ~= '' then
            if signature == '' then
                signature = zeile
            elseif checknr == '' then
                checknr = zeile
                text1   = zeile
            else
                table.insert(reports,zeile)
                text1 = text1 .. zeile
            end
        end
    end    

    local md5key1 = '---'
    if #signature > 5 then
        md5key1 = self:decrypt(pub_key1, signature )
    end
    local md5key  =  md5lib.hash(text1)

    if md5key == md5key1 then
        self.report_is_integer = 1
    else
        self.report_is_integer = 0
    end
    

    if file_open == nil then
--        for i,z in ipairs(reports) do print(z) end
        return(reports)
    end

    local checknr0 = SERIALNUMBER .. '/' .. string.format('%1u',akt_count) .. '/' .. string.format('%1u',reportnr)
    if true and (checknr0 ~= trim(checknr)) then  -- Counter und Counter-Eintrag in der newreport passen nicht zusammen
        local akt_zeit_hm_tmp = self:date('%H%M',self:time())
        my_write_filedata1('signaerror.txt',sd_dir..'/signaerror.txt',checknr0.."\n"..checknr.."\n"..akt_zeit_hm_tmp.."\n\n",add_write)
        local pos1
        local pos2   --  vielleicht nur, weil beim letzten Ticketdruck der Strom abgeschaltet war
        pos1,pos2 = string.find(checknr,"/.-/")
        if (pos1 == nil) or (pos2 == nil) or (not reconstruct == 1) then 
--            local pass3 = 33   -- d.h. nichts machen
            self.report_is_integer = 0
        else
            local report_count = tonumber( string.sub(checknr,pos1+1,pos2-1) )
            local count_diff   = report_count - akt_count
            if count_diff > 200 or count_diff < -200 then
                self.normal_test = "1340"
error2("wr66")
                self:write_report(reports,self:date("%y%m%d%H%M%S").."X","0,07")
error2("wr67")
--                local pass3 = 33   -- d.h. nichts machen
                self.report_is_integer = 0
            else
                if count_diff > 0 then
                    local lost_amount  = count_diff / POINTS_PER_EURO + 1   --  verlorenen Betrag rekonstruieren
                    lost_amount        = math.floor(0.1*lost_amount) * 0.1 + 0.01   -- der eine Cent dient zur Anzeige, dass hier ein Problem war, und der Betrag rekonstruiert worden ist
                    lost_amount        = string.gsub(lost_amount,"%.",",")
                    local current      = CURRENTSHORT
                    if lost_amount < 0.05 then
                        current = "Q"
                    end
--                    self:write_report(reports,self:date("%y%m%d%H%M%S").."C",lost_amount)
                    self.normal_test = "1341"
error2("wr68")
                    self:write_report(reports,self:date("%y%m%d%H%M%S")..current,lost_amount)
error2("wr69")
--                    reports = self:read_report()  -- nochmal auslesen, aber nicht mehr zu rekonstruieren versuchen
                else
                    self:update_counter( count_diff )
                    self.normal_test = "1342"
error2("wr70")
                    self:write_report(reports,self:date("%y%m%d%H%M%S").."X","0,00")
error2("wr71")
                    local art_der_signierung = 0
                    if self:check_flash_signature() == 0 then
                        self:check_flash_signature('read_report')
                        art_der_signierung = art_der_signierung + 2
                    end
                    if not (self.report_is_integer == 1) then
                        self.report_is_integer = 0
                        art_der_signierung = art_der_signierung + 3
                    end
                    self.normal_test = "1343"
error2("wr72")
                    self:write_report(reports,self:date("%y%m%d%H%M%S").."X","0,0"..tostring(art_der_signierung))
error2("wr73")
                end
                local pin_defs = self.pin_definitions:read()
                if (SERIALNUMBER == '0553227') or (SERIALNUMBER == '0123456') or not (string.find(pin_defs,"w56dfgsu1") == nil) then  -- bypass battery alert
                    return(reports)
                end
                return(nil)   --  dies bedeutet, dass man einen Low Batterie Alert anbringt und aussteigt
            end
        end
    end
         
--    self.report_is_integer = 1  --  fuer Testzwecke   @@

    return(reports)
        
end

--**********************************************************************

function App.check_for_battery_alert (self)

    local tn   = self.lang_nr_for_report
    local batt = my_read_filedata1('battery.txt',sd_dir..'/battery.txt')
    if batt == nil then
        batt = ""
    end
       
    if file_open == nil then
        return(0)
    end


    if #batt > 10 then
    
        my_write_filedata1("battery.txt",sd_dir..'/'.."battery.txt"," \n",over_write)  --  schon mal die Datei battery.txt ueberschreiben, bevor gedruckt wird!
        local batteryinfo =     --   denn wenn die Batterie schwach ist, dann stuerzt das Geraet ja beim Drucken des Battery-Alert eh wieder ab
' \n' ..
' \n' ..
'QQx1d@\n' ..
'QW\n' ..
'QW\n' ..
'QQx1ba0QQx1d!QQx00\n' ..
self:ext_line( t('Please check',tn) ).. "\n" ..
self:ext_line( t('battery state',tn) ).. ".\n" ..
' \n' ..
self:ext_line( t('Then restart',tn)   ).. ".\n" ..
'QQx1d!QQx00\n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
' \n' ..
'QQx1dVQQx01\n' ..
'QW\n' ..
'QW\n' ..
'QQx1d@\n'
    

        local batteryinfo_table = {}
        for zeile in string.gmatch(batteryinfo,'[^\n]+') do
            table.insert(batteryinfo_table,zeile)
        end
        self:send_to_printer1(batteryinfo_table)  --  nicht die self_to_printer Funktion nehmen, denn Geraet wird ja gleich disabled,
        return(1)                                 --  und alle Timer werden ausgeschaltet. self_to_printer funktioniert aber nur mit Timer
    else
        return(0)
    end
    
end

--**********************************************************************

function App.write_report (self,reports,datum,betrag)

    self.last_print_datum = tostring(datum)

    if betrag ~= nil then
        table.insert(reports,self.last_print_datum..'  '..trim(betrag))
    end
    
    local reportnr = self.reportnumber_tmp
    if reportnr == nil then
        reportnr = fcheck(0,tonumber,self.reportnumber:read())
--    print('Q1',reportnr)
    end
    if reportnr == nil then
        reportnr = 0
    end
    self.reportnumber_tmp = nil
    local akt_count = self:read_counter()

    local checknr   = SERIALNUMBER .. '/' .. string.format('%1u',akt_count) .. '/' .. string.format('%1u',reportnr)
--    print('QC',checknr,reports)
    local text1     = checknr
    for i,zeile in ipairs(reports) do
        text1 = text1 .. zeile
    end
    
    local text_normal = normal_signs(text1)
    if text_normal ~= '' then
        error1("XB "..self.normal_test.." "..string.sub(text1,1,100))
        text1 = ''
    end
    
    local signature = '---'
    if self.report_is_integer == 1 then
        local md5key  = md5lib.hash(text1)
        error2("signsign01")
        signature     = self:crypt(sec_key1, md5key)
        error2("signsign02")
    end
    
--    self.debug1:write("WRITEREPORT_SIGNA"..signature)
    
    text = signature .. '\n' .. checknr .. '\n'
    for i,zeile in ipairs(reports) do
--        print("DDD",zeile)
        text = text .. zeile .. '\n'
    end
    
    
--    print(text)
    
--    print(self:crypt(sec_key3,text,80))
    
        error2("reportsign01")
    my_write_filedata1('newreport.txt',sd_dir..'/newreport.txt',self:crypt(sec_key3,text,80),over_write)
        error2("reportsign01")
        
--    print("QWWWW",self.there_is_no_sdcard)
    if math.random(1,25) == 1 then
        text = my_read_filedata1('newreport.txt',sd_dir..'/newreport.txt')
        if text == nil or text == "" then
            self.there_is_no_sdcard = 1
        end
    end
--    print("QWWWW",self.there_is_no_sdcard)
    
    return(1)
        
end

--**********************************************************************

function App.generate_report (self) local s, r = xpcall(App.p_generate_report,function(e) error1(e) end,self) return(r) end

--**********************************************************************

function App.p_generate_report (self)

--  1.  Report ermitteln

    local tn = self:sync_report_data("lang",self.lang_nr_for_report)

    local reportnr = self:sync_report_data("reportnr",fcheck(0,tonumber,self.reportnumber:read()))
    if reportnr == nil then
        reportnr = 0
    end
    reportnr = reportnr + 1
    
    self.report_is_integer = self:sync_report_data("rinteger",self.lang_nr_for_report)
    
    local short_report
    local stxt1
    local report_txt
    local reportfile
    local zaehler = 0
    self.mz = ""

    error2("---")

    error2_shift()

    while 0 == 0 do


        self:interrupt_report_computation("Setup main loop 1 for report, WAIT26")
        error2("mainloop0004")


        zaehler = zaehler + 1
        local spalten = self:generate_report_spalten_berechnen(tn)
        if spalten == nil then
            return(0)
        end


        reportfile        = 'ticketreport_' .. SERIALNUMBER .. '_' .. self:date('%y%m%d') .. "_" .. self:date('%H%M%S')
        reportfile        = reportfile .. '_' .. string.format('%04u',reportnr) .. '.txt'
        self.jahresordner = self:date('%y')

        self:interrupt_report_computation("Setup main loop 2 for report, WAIT26")
        error2("mainloop0005")

        report_txt        = self:generate_report_write_complete(spalten,reportnr,tn)

        if report_txt == nil then
            return(0)     -- Reportberechnung wurde aus bestimmten Gruenden bei der Spaltenberechnung abgebrochen
        end
        
        self:interrupt_report_computation("Setup main loop 3 for report, WAIT26")
        error2("mainloop0006")

        short_report, stxt1  = self:make_short_report()  --  in stxt1 steht der alternative Text zum QR-Code drin
        error2("mainloop0007")
        
--        print(sr32)

        if #short_report < MAXTRANSMIT then   --  dann ist noch Platz fuer 22 zeichen fuer die URL-Angabe
            break
        end
        

        local max_zeilen = self.report_data["maxzeilen"]   --  Short report ist zu lang, daher die max Zeilenzahl verkuerzen
        if max_zeilen < 10 then  --  aber nicht unter diese Zeilenzahl
            break
        end

        self.mz = self.mz .. string.format("%02u",tonumber(max_zeilen))
        self.report_data["maxzeilen"]  = max_zeilen - 8  --  Report um 8 zeilen verkuerzen
        self.report_data["signastamp"] = nil
        self.report_data["location"]   = nil
        self.report_data["whoadm"]     = nil
        self.report_data["reports"]    = nil
        self.reporthashes              = self.reporthashes0  --   falls in make_short_report schon geandert wurde

    end
    
--    print("SR SR",type(short_report),short_report)
        
        
--  2.   den short report in kodierter Foerm noch dranhaengen  (braucht man eig nicht)

        error2("mainloop0008")

    short_report1 = short_report
    local zeilenlaenge = PAPIERBREITE    -- 
    while zeilenlaenge ~= nil do
        table.insert(report_txt,string.sub(short_report1,1,zeilenlaenge))
        short_report1 = string.sub(short_report1,zeilenlaenge+1)
        if #short_report1 == 0 then
            break
        end
    end

--  2a.  nur fuer Testzwecke

    table.insert(report_txt," ")
    table.insert(report_txt,string.format("%06u",zaehler*100000+#short_report) .. "  " .. self.mz)

--  3. Abspeichern report



        error2("mainloop0009")


    self.reportnumber:write(string.format("%1u",reportnr))
    self.reportnumber_tmp = reportnr
    self.normal_test = "1344"
error2("wr74")
    self:write_report(self.report_data["reports"])
error2("wr75")
    local reportfile = self:write_report_to_file(report_txt,reportnr)
error2("wr76")

--  4.  startsection fuer print of report

    local rtxt1 = {
--'QQx1d@',    --  printer initialize  (oder 1b@??, haben wir jemals den Printer initialisiert??)
--'QW',
--'QW',
'QQx1ba0QQx1d!QQx00',     --  1b (ESC) a0: linksbuendig, 1d (GS) ! 0: Schriftgroesse 0
}

        error2("mainloop0010")


--  5. Laengenangabe machen für shortreport
    
    self.sr = short_report  --  fuer test

    short_report = "https://12park.de/x/" ..  string.sub(short_report,1,300)
    local len_short_report = #short_report + 3
    local stxt_part_len    = math.floor(len_short_report/5)
    local len_angabe       = "QQx00"
    if len_short_report > 255 then
        len_angabe       = "QQx01"
        len_short_report = len_short_report - 256
    end
    len_short_report  = string.format("%02x",len_short_report)
    len_angabe        = "QQx" .. len_short_report .. len_angabe

    local short_report_1 = string.sub(short_report,0*stxt_part_len+1,1*stxt_part_len)
    local short_report_2 = string.sub(short_report,1*stxt_part_len+1,2*stxt_part_len)
    local short_report_3 = string.sub(short_report,2*stxt_part_len+1,3*stxt_part_len)
    local short_report_4 = string.sub(short_report,3*stxt_part_len+1,4*stxt_part_len)
    local short_report_5 = string.sub(short_report,4*stxt_part_len+1,5*stxt_part_len+9999)


--  6. QR-Code Ausgabe f shortreport

    table.insert(stxt1,1,' ')
    table.insert(stxt1,1,string.format("%03u",reportnr))
    table.insert(stxt1,1,' ')
    table.insert(stxt1,'QW')

    if self.model_nr == 1 then   --  QR-Code Ausgabe nur auf neuen Geraeten

        table.insert(stxt1,'QQx1ba1')                        --  mittig drucken
        table.insert(stxt1,'QQx1d(k40QQx31QQx41QQx31QQx00')  --  QR-Code Style:  31  (31,32)
        table.insert(stxt1,'QQx1d(k30QQx31QQx43QQx04')       --  QR-Code Pixelvielfachheit: 4
        table.insert(stxt1,'QQx1d(k30QQx31QQx45QQx34')       --  QR-Code Correction 34  (2f..34)
        table.insert(stxt1,'---DEL---6')    -- damit werden die naechsten 6 Zeilen nicht mit Zeilenvorschub versehen
        table.insert(stxt1,'QQx1d(k'..len_angabe..'QQx31QQx50QQx30')
        table.insert(stxt1,short_report_1)  -- Speichern QR-Code
        table.insert(stxt1,short_report_2)
        table.insert(stxt1,short_report_3)
        table.insert(stxt1,short_report_4)
        table.insert(stxt1,short_report_5)
        table.insert(stxt1,string.format("%03u",reportnr))
    --table.insert(stxt1,'QQx1d(k'..len_angabe..'QQx31QQx50QQx30' .. short_report .. string.format("%03u",reportnr) .. 
        table.insert(stxt1,'QQx1d(k30QQx31QQx51QQx30')       --  Ausdrucken QR-Code
        table.insert(stxt1,'QW')
        table.insert(stxt1,'QW')
        
    end


        error2("mainloop0011")


    table.insert(stxt1,' ')
    table.insert(stxt1,' ')
    table.insert(stxt1,' ')
    table.insert(stxt1,' ')
    table.insert(stxt1,' ')

    table.insert(stxt1,'QQx1dVQQx01')                    --  paper cut half
    table.insert(stxt1,'PAUSE1311')
    table.insert(stxt1,'QW')
    table.insert(stxt1,'QW')
    table.insert(stxt1,'QQx1d@')                         --  printer initialize  (oder 1b@??, haben wir jemals den Printer initialisiert??)
    table.insert(stxt1,'QW')
    table.insert(stxt1,'QW')
    table.insert(stxt1,' ')

    local prbuf1 = {}
    local prbuf2 = {}

    for i,zeile in ipairs(rtxt1) do
        table.insert(prbuf1,zeile)
        table.insert(prbuf2,zeile)
    end

    if PRINT_BF_EXIST == 1 then
        for i,zeile in ipairs(report_txt) do
            table.insert(prbuf1,string.sub(zeile,1,32))
        end
    end


    for i,zeile in ipairs(stxt1) do
        table.insert(prbuf1,zeile)
        table.insert(prbuf2,zeile)
    end

    self:add_to_printbuffer(  prbuf1 )
    self:add_to_printbuffer2( prbuf2 )
        error2("mainloop0012")

    return(reportfile)

end

--************************************************************************

function App.add_to_printbuffer (self,text1)

--    if file_open ~= nil then

--        print("WWWW",self.squeeze_out)

        if self.squeeze_out == nil then
            local printtext = self:decrypt( pub_key3, my_read_filedata1('printbuffer.txt',sd_dir..'/printbuffer.txt') )
            if printtext == nil then
                printtext = ""
            end
            for i,zeile in ipairs(text1) do
                printtext = printtext .. zeile .. "\n"
            end
        error2("addsign01")
            printtext = self:crypt(sec_key3,printtext,80)  -- weil da Klartext drin ist, muss verschluesselt werden
        error2("addsign02")
            my_write_filedata1('printbuffer.txt', sd_dir..'/printbuffer.txt',printtext,over_write)
        else  --   der Printbuffer ist leer, weil alle vorraetigen Reports schon ausgedruckt sind. Deswegen muss dieser nicht mehr gelesen werden
            print("SQUEEZE_OUT")
            self:send_to_printer(text1)  --  direkter Druck, anstelle in den Printbuffer zu schreiben
--            self.report_wird_gedruckt:set_visiable(0)
        end
        
--    end

end

--************************************************************************
    
function App.add_to_printbuffer2 (self,text1)

    local printtext2 = my_read_filedata1('printbuffer2.txt',sd_dir..'/printbuffer2.txt')
    if printtext2 == nil then
        printtext2 = ""
    elseif #printtext2 < 16 then
        printtext2 = ""
    else
        printtext2 = self:decrypt( pub_key3, printtext2 )
    end
    
    for i,zeile in ipairs(text1) do
        printtext2 = printtext2 .. zeile .. "\n" 
    end
        error2("addbuf01")
    printtext2 = self:crypt(sec_key3,printtext2,80)  -- weil da Klartext drin ist, muss verschluesselt werden
        error2("addbuf02")
    my_write_filedata1('printbuffer2.txt', sd_dir..'/printbuffer2.txt',printtext2,over_write)

end

--************************************************************************

function App.write_report_to_file (self,report_txt,reportnr)

    local filetxt = ''
    for i,zeile in ipairs(report_txt) do
        filetxt   = filetxt..zeile..'\n'
    end
--    filetxt = filetxt..' \n'
    local klartext_report = nil
    if file_open == nil then
        klartext_report = "report_" .. SERIALNUMBER .. "_" .. string.format("%03u",reportnr) .. ".txt"
    end
    local filename  = self:store_encrypted_file(filetxt,md5lib.hash(self.signature),klartext_report)

    return(filename)

end

--************************************************************************

function App.sync_report_data (self,fname,val)

    if self.report_data[fname] ~= nil then
        return(self.report_data[fname])
    else
        self.report_data[fname] = val
        return(val)
    end
    
end        

--************************************************************************

function App.write_report_header_template (self,tn)

    local text1 = {}  -- Header
    for zeile_text1 in string.gmatch( self:ext_line(t('Protokoll verkaufte Parkscheine',tn)) ,'[^\n]+') do
        table.insert(text1,zeile_text1)
    end
    table.insert(text1,self.location1:read())
    table.insert(text1,t('PROTOKOLLNR',tn)..' '..SERIALNUMBER..' / _SERNO_')
    table.insert(text1,t('erzeugt',tn)..' _DATUM_ _ZEITS_')
    table.insert(text1,'_WHO_')

    self:sync_report_data("header",text1)
    self:sync_report_data("signatitle",string.sub(t('Signatur',tn).."          ",1,10))
    self:sync_report_data("footer",self:website())
    
    self:sync_report_data('sformat',t('ZEITS',tn))

    if (self.who_logged_in_as_admin ~= nil) and (self.who_logged_in_as_admin ~= '') then
        self.report_data['whoadmin'] = self.who_logged_in_as_admin
    else
        self.report_data['whoadmin'] = ""
    end

end
    
--************************************************************************

function App.write_report_data (self,reportnr,tn)

    self:sync_report_data("reportnr",      reportnr)
    self:sync_report_data("reportdatum",   self:time())
    self:sync_report_data("who",           t('VON',tn))
    local who_admin   = ""
    if (self.who_logged_in_as_admin ~= nil) and (self.who_logged_in_as_admin ~= '') then
        local who_admin = self.who_logged_in_as_admin
    end
    who_admin = self:sync_report_data("whoadmin",who_admin)

    local text1 = {}
    
--    print(self.report_data['header'],reportnr)
    for i,zeile in ipairs(self.report_data['header']) do
        local zeile1 = zeile
        zeile1       = string.gsub(zeile1,"_SERNO_",string.format("%03u",self.report_data['reportnr']))
        zeile1       = string.gsub(zeile1,"_DATUM_",self:date(self.report_data['dformat'],self.report_data['reportdatum']))
        zeile1       = string.gsub(zeile1,"_ZEITS_",self:date(self.report_data['sformat'],self.report_data['reportdatum']))
        zeile1       = string.gsub(zeile1,"_WHO_",  self.report_data['who'])
        if zeile1 ~= "" then
            table.insert(text1,zeile1)
        end
    end

    if who_admin ~= "" then
        table.insert(text1,string.sub(who .. ": " .. who_admin .. "                                ",1,32))
    end
   
    return(text1)
   
end

--************************************************************************

function App.spalten_zweispaltig_formatieren (self,spalten)

    local text2 = {}
    for i = 1,2,1 do
        while #spalten[i] > 0 do
            if trim(spalten[i][ #spalten[i] ]) == '' then
                table.remove(spalten[i])
            else
               break
            end
        end
    end
            
    
    local add_leerzeile = #spalten[1] >= #spalten[2]
    if add_leerzeile then
        add_leerzeile = not (string.sub(spalten[1][#spalten[1]],1,12) == "            ")
    end        

    while #spalten[1] > 0 or #spalten[2] > 0 do   --  hier die Spalten zusammenfuehren
        zeile = table.remove(spalten[1],1)
        if zeile == nil then
            zeile = ''
        end
        zeile = string.sub(zeile .. '                ',1,16)
        --print(zeile, table.remove(spalten[2],1))
        zeile = zeile .. '  ' .. (table.remove(spalten[2],1) or '')
        table.insert(text2,zeile)
    end

    return(text2)
    
end

--************************************************************************

function App.write_report_text (self)

    local text1 = {}
    for i,zeile in ipairs(report_data['header']) do
        local zeile1 = string.gsub(zeile,"-SERNO-",report_data['reportnr'])
        zeile1       = string.gsub(zeile,"-DATUM-",self:date(report_data['dformat'],report_data['reportdatum']))
        zeile1       = string.gsub(zeile,"-ZEITS-",self:date(report_data['sformat'],report_data['reportdatum']))
        zeile1       = string.gsub(zeile,"-WHO-",  report_data['who'])
        if zeile1 ~= "" then
            table.insert(text1,zeile1)
        end
    end
               
end


--************************************************************************
   
--  die Spalten sind fertig, jetzt das eigentliche Protokoll schreiben

function App.generate_report_write_complete (self,spalten,reportnr,tn)

    local header_template = self:write_report_header_template(tn)
    local header          = self:write_report_data(reportnr,tn)
    local spaltentext     = self:spalten_zweispaltig_formatieren(spalten)

    self:interrupt_report_computation("Spalten fertig, WAIT26")

    error2("signa0001")
    local signashort = self:make_signa_report(header,spaltentext,reportnr)
    error2("signa0002")
    local signastamp = self:sync_report_data("signastamp", signashort )
    
    if (signashort ~= signastamp) then
        return(nil)
    end
    
--  5. jetzt den Text des Reports schreiben

    local text1 = header

    if self.report_is_integer ~= 1 then
        table.insert(text1,' ')
        table.insert(text1,t('Nicht signierter Ausgabereport.',tn))
        table.insert(text1,t('Nur nachrichtliche Werte.',tn))
        table.insert(text1,t('Kann nicht als Umsatznachweis',tn))
        table.insert(text1,t('fuer die Steuerbehoerden',tn))
        table.insert(text1,t('verwendet werden.',tn))
    else
        local sr2 = self.report_data['signatitle'] .. self.signature
        table.insert(text1,string.sub(sr2,1,32))
        table.insert(text1,string.sub(sr2,33,64))
    end

    table.insert(text1,' ')
    for i,zeile in ipairs(spaltentext) do
        table.insert(text1,zeile)
    end
    
    if string.sub(text1[#text1],1,14) ~= "              " then
        table.insert(text1,"")
    end

    table.insert(text1,self:website())
    table.insert(text1,"")
    
    self:interrupt_report_computation("Report fertig, WAIT26")
        error2("signa0003")

    return(text1)

end

--************************************************************************

function App.interrupt_report_computation (self,text)

    if file_open ~= nil then
        start_timer(26,WAIT_26,0,1)
        coroutine.yield()
    else
        if text ~= nil then
            notify(text)
        end
    end

end

--************************************************************************

function App.generate_report_spalten_berechnen(self,tn)

    self.normal_test1 = "1435"
    local reports               = self:sync_report_data("reports",      self:read_report())
--    for i,z in ipairs(reports) do print(z) end
    self.report_is_integer      = self:sync_report_data("rinteger",     self.report_is_integer)

    if #reports == 0 then
        self:reset_squeeze()
        return(nil)
    end

    error2("generate9001")
    
    local mwst                  = self:sync_report_data("ust",          fcheck(0,tonumber,self.tax_vat:read()))
    local max_zeilen            = self:sync_report_data("maxzeilen",    self:update_pruefcode())
    local dformat               = self:sync_report_data("dformat",      t("DATUM",tn))
    local zformat               = self:sync_report_data("zformat",      t("ZEIT",tn))

    self.mz = self.mz .. "(" .. string.format("%02u",tonumber(max_zeilen)) .. ")"
    
    max_zeilen                  = math.max(max_zeilen-6,10)
    local spalten               = { {}, {} }
    self.nr                     = 1  --  Spaltennummer, bei self.nr = 3 ist das Ticket fertig
    local contains_real_entries = 0
    local zeile                 = ''
    self.heartbeat_intervals    = {}

    error2("generate9002")

    table.insert(self.heartbeat_intervals,"")
    if self.squeeze_out == nil then   --  der Report wird nicht voll, also gleich zurueck
        if #reports < max_zeilen - 2 then
            self:reset_squeeze()
            return(nil)
        end
    end

    error2("generate9003a")

    mwst = (mwst * 1000 + 1)/100
    if math.floor(mwst % 10) == 0 then

    error2("generate9003b")
        mwst = string.format('%2u',math.floor(mwst/10))
    else

    error2("generate9003")
        mwst = trim(string.format('%4.1f',mwst/10))
        mwst = mwst:gsub('%.',',')
    end

    error2("generate9004")

    self.report_wird_gedruckt:set_visiable(1)

    self:interrupt_report_computation("Report wird gedruckt, WAIT26")
    error2("generate0001")

    self.fehltage_vorhanden     = 0 == 1
    self.total                  = 0.00   --  Ticketsumme
    self.datum                  = ''     --  Aktuelles
    self.datum_raw              = ''     --    Datum
    self.heartbeat_series       = {}
    self.last_underline         = 1
    self.zeilen_for_shortreport = {}
    self.summe                  = 0.00
    self.heartbeats             = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}

    local has_no_act_datum_line = nil
    local alle_report_zeilen_untergebracht = 1

    local zaehler = 0
    while 0 == 0 do  -- gehe durch alle newreport-Zeilen
        
        zaehler         = zaehler + 1
        self.datum0     = self.datum         --    Vorhergehendes
        self.datum0_raw = self.datum_raw     --    Datum

        if zaehler % 10 == 0 then  --  alle 10 Zeilen 
            if file_open ~= nil then
                start_timer(26,WAIT_26,0,1)
                error2("newreport_0010")
                coroutine.yield()
            end
        end

        self:rearrange_spalten(spalten,max_zeilen) -- ggfs. Zeilen schon in die Spalte 2 schaffen

--                error2("newreport_0011")

        if #reports == 0 then
            break
        end

--                error2("newreport_0012")
        local stunde = self:fetch_next_report_line(reports,spalten,max_zeilen)

--                error2("newreport_0013")

        if self.datum0 ~= self.datum then    --  neue Datumsueberschrift anbringen

            if #spalten[2] > max_zeilen - 12 then
                alle_report_zeilen_untergebracht = 0
                break
            end    
                
--            for i,z in ipairs(self.heartbeats) do print("UUU",z) end
            self:underline_nachgetragen(spalten)
   
--                error2("newreport_0014")


            local fehltage_vorhanden = 0
            if self.datum0 ~= '' then
                self:add_sum_line(spalten)
                fehltage_vorhanden = self:fehltage_eingetragen(reports,spalten,dformat)
            end 
            
            self:datum_eingetragen(spalten)
            
--                error2("newreport_0015")

            self.summe            = 0.00
            self.heartbeats       = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
            has_no_act_datum_line = 1
                
--            self:rewrite_to_reports(reports)  --  Zeile wird zurueckgeschrieben und noch mal bearbeitet

        end
        
--                error2("newreport_0016")


        if #spalten[2] > max_zeilen - 8 then
            alle_report_zeilen_untergebracht = 0
            break
        end
        
--                error2("newreport_0017")


        self.heartbeats[stunde] = 1
        self:new_report_line(spalten)
        if self.current ~= "." then
            has_no_act_datum_line   = nil
        end

--                error2("newreport_0018")

    end
    

    self:rewrite_to_reports(reports,has_no_act_datum_line,alle_report_zeilen_untergebracht)


--                error2("newreport_0019")



    self:underline_nachgetragen(spalten)
    self:add_sum_line(spalten)


--                error2("newreport_0020")


    if #self.heartbeat_intervals % 2 == 1 then  --  Intervalle muessen immer mit Anfang und Ende angegeben werden
        table.remove(self.heartbeat_intervals)
    end

    local zaehler1 = 0
    while 0 == 0 do
        zaehler1  = zaehler1 + 1
        local zz = self.heartbeat_intervals[zaehler1]
        if zz == nil then
            break
        end
--        print("HB",zz)
    end


--                error2("newreport_0021")


    if zaehler == 1 or self.contains_real_entries == 0 then    --  keine Zeilen verarbeitet
        self:reset_squeeze()
        return(nil)
    end

    if #reports == 0 then
        if self.squeeze_out == nil then   --  um zu verhindern, dass 'halbe' reports ausgedruckt werden
            self:reset_squeeze()
            return(nil)
        end
    end    


--                error2("newreport_0022")


    self:add_netto_ust_total(spalten,max_zeilen)

--                error2("newreport_0023")
    return(spalten)


end

--**********************************************************************

function App.rearrange_spalten(self,spalten,max_zeilen)

    while #spalten[1] > max_zeilen and #spalten[2] == 0 and #string.gsub(spalten[1][#spalten[1]],' ','') == 0 do
        table.remove(spalten[1])    --  Leerzeilen am Ende von Spalte 1 loeschen
    end

    if #spalten[1] > max_zeilen or #spalten[2] > 0 then    -- Zeilen in die zweite Spalte schaffen, wenn noetig
        while 0 == 0 do
            if 
                     #spalten[1] == 0 or #spalten[1] > max_zeilen
                or   #spalten[1] > 0 and string.sub(spalten[1][ #spalten[1] ],11,12) == ' -'
                or   #spalten[1] > 0 and string.sub(spalten[1][ #spalten[1] ],1,2) == '--'
                or   #spalten[2] > 0 and string.sub(spalten[2][1],1,2) == '--'
                or   #spalten[2] > 0 and #string.gsub(spalten[2][1],' ','') > 0            and string.sub(spalten[2][1],1,1) == ' '
                or   #spalten[1] > 0 and #string.gsub(spalten[1][#spalten[1]],' ','') > 0  and string.sub(spalten[1][#spalten[1]],1,1) == ' '
                or   #spalten[1] > 0 and string.sub(spalten[1][#spalten[1]],3,3) == "." and string.sub(spalten[1][#spalten[1]],6,6) == "."  
--                or   #spalten[1] > 0 and string.sub(#spalten[1][ #spalten[1] ],6,6) ~= ' '
            then
                local z = table.remove(spalten[1])
                table.insert(spalten[2],1,z)
            else
                break
            end
        end
        self.nr = 2
        if trim(spalten[2][1]) == '' then
            table.remove(spalten[2],1)
        end
    end
        
end


--**********************************************************************

function App.fetch_next_report_line (self,reports,spalten,max_zeilen)

    local zaehler1 = 1
    local stunde   = nil
    
    while zaehler1 < 100 do   -- Format Check fuer Zeilen im newreport.txt
        zaehler1       = zaehler1 + 1
        self.zeile     = table.remove(reports,1)        --   hier wird die Zeile geholt
        if self.zeile == nil then   --  dieser wichtige Fehler wurde erst am 25.8.25 korrigiert
            break
        end
        self.datum_raw = string.sub(self.zeile,1,14)    --   im Format yyyymmddHHMMSS
        self.current   = string.sub(self.zeile,15,15)
        if string.sub(self.zeile,16,16) == " " then
            self.betrag = trim( string.sub(self.zeile,16) )
        else  
            self.betrag = trim( string.sub(self.zeile,18) ) -- wegen eines Bugs beim Eintrag in die newreport 
        end
        self.betrag_remark = self.betrag
        self.betrag        = string.gsub(self.betrag,"%a","0")   --  der Betrag kann ja auch Buchstaben enthalten von der PIN-Eingabe fuer Freitickets
        self.betrag        = self.betrag:gsub(',','.')
        local x1           = fcheck(-99,tonumber,self.datum_raw)
        local x2           = fcheck(-99,tonumber,self.betrag) 
        if x1 ~= -99 and x2 ~= -99 then
--            print(self.zeile)
            stunde = tonumber(string.sub(self.zeile,9,10))+1
--            self.heartbeats[tonumber(string.sub(self.zeile,9,10))+1] = 1
            break   --  nur weiter, wenn die geholte Zeile korrekt war, sonst einfach naechste Zeile holen
        end
    end
    
    
--    print(self:print_heartbeat())
    
    if stunde ~= nil then    

        self.uhrzeit = self:date(self.report_data["zformat"],tonumber(self.datum_raw))
        self.datum   = self:date(self.report_data["dformat"],tonumber(self.datum_raw))
    
        if self.current ~= "." then
            table.insert(self.zeilen_for_shortreport,1,{self.datum_raw,self.current,self.betrag,self.betrag_remark})
        end
--        print("XV",self.zeile,self.betrag,#self.zeilen_for_shortreport)
--        for i,z in ipairs(self.zeilen_for_shortreport) do print ("WWW",z[1]) end

        self:update_heartbeat_intervals()
        
    end
    
    return(stunde)

end

--**********************************************************************

function App.print_heartbeat(self)

    local tt = ""
    for i,zeile in ipairs(self.heartbeat_intervals) do
        tt = tt .. zeile .. ","
    end
    
    return(tt)
    
end

--**********************************************************************

function App.update_heartbeat_intervals (self)

    local datum_std  = string.sub(self.zeile,1,10)
    local akt_stunde = tonumber( string.sub(self.zeile,9,10) )

    if self.heartbeat_intervals[1] == "" then   --  es ist noch kein Intervall geoeffnet
--        if string.sub(self.datum_raw,1,10) ~= string.sub(self.datum0_raw,1,10) and self.current == "." then
        if self.current == "." then
            if self.heartbeat_exclude ~= datum_std then    --  Intervall oeffnen, aber nur, wenn in der betreffenden Stunde nicht schon ein reales Intervall
                if self.heartbeat_intervals[2] ~= datum_std then
                    self.heartbeat_intervals[1] = datum_std    --  aufgemacht wurde
                    table.insert(self.heartbeat_intervals,1,datum_std)
--                    self.datum0     = self.datum         --  Vorhergehendes
--                    self.datum0_raw = self.datum_raw     --  Datum,  sonst gibt es Fehler, weil ggfs ein neues Intervall aufgemacht wird
                end
            end
        else
            self.heartbeat_exclude = datum_std   --  diese Stunde markieren, hier braucht also kein Intervall geoeffnet werden
        end
        
    else   --  bestehendes Intervall
    
        local akt_stunde0 = tonumber( string.sub(self.heartbeat_intervals[1],9,10) )   --  letzte eingetragene Stunde
        local same_day    = string.sub(datum_std,1,8) == string.sub(self.heartbeat_intervals[1],1,8)

        if akt_stunde == "00" then   -- special Berechnung corner case 
            akt_stunde = "24"  --  wenn es der naechste Tag ist, oder die Stunde nicht 23 ist (dann tritt in jedem Fall ein: datum_std- datum_std0 > 1.
            same_day   = 1 == 1
            if akt_stunde0 == "23" then --  hier muss tatsaechlich der Tag geprueft werden
                local datum_std0 = string.sub(self.heartbeat_intervals[1],1,10)   --  letzte eingetragene Stunde
                if self:time(datum_std+"0030")  - self:time(datum_std0+"0030") > 86400 + 1000  then --  es ist mehr als einen Tag Differenz 
                    akt_stunde = "25"  --  aber diese time-Berechnung will man echt nur machen, wenn es noetig ist
                    same_day   = 0 == 1
                end
            end
        end
                 
--        print("AS",akt_stunde0,akt_stunde)
        if same_day and akt_stunde == akt_stunde0 + 1 then 
            self.heartbeat_intervals[1] = datum_std    --  Intervall einfach erweitern
        elseif not same_day or akt_stunde ~= akt_stunde0 then
            if self.current == "." then
                table.insert(self.heartbeat_intervals,1,datum_std)  --  da ist eine Luecke, also wirklich ein neues Intervall anfangen
                table.insert(self.heartbeat_intervals,1,datum_std)
            end
        else  --  hier gilt: akt_stunde == akt_stunde0
            if self.current ~= "." then
                if string.sub(self.heartbeat_intervals[2],1,10) == string.sub(self.heartbeat_intervals[1],1,10) then 
                    table.remove(self.heartbeat_intervals,1)   --  das angebrochene Intervall kann entfernt werden, da ein realer Eintrag vorliegt,
                    self.heartbeat_intervals[1] = ""           --  und das Intervall erst noch aus einem Punkt besteht
                    self.heartbeat_exclude = datum_std   --  diese Stunde markieren, hier braucht also kein Intervall geoeffnet werden
                end
            end
        end
    end

--    for i,z in ipairs(self.heartbeat_intervals) do print("-->",z) end


end

--**********************************************************************

function App.xxadd_to_compact_report (self)

    for i,zeile in ipairs(report_data['reports']) do
        local datum    = tonumber(string.sub(zeile,2,4))
        if start_datum == "" then
            start_datum = datum
            info_data   = string.format("%02x",start_datum)  -- Startjahr fuer den Report
        end                          --   alle Jahresangaben sind relativ dazu
        local tagesnr = ( 12 * (datum - start_datum) + tonumber(string.sub(zeile,5,6)) ) 
        if monatsnr0 ~= monatsnr then
            monatsnr0 = monatsnr
            zeitplus  = 1
        end
        local tagesminute = 31 * 24 * 60 * monatsnr
                             +    24 * 60 * tonumber(string.sub(zeile,7,8))
                             +         60 * tonumber(string.sub(zeile,9,10))
                             +              tonumber(string.sub(zeile,11,12))
--        if zeitplus == 1 then
--            monatsminute * 
                             
                             
--        local monatsnr = 12 * (datum - start_datum) + tonumber(string.s
        local monatsnr = 12 * (datum - start_datum) + tonumber(string.sub(zeile,5,6))
        table.insert(info_data,zeile)
    end

end

--**********************************************************************

function App.add_sum_line (self,spalten)

    if #spalten[self.nr] > 0 then   --  es darf nicht eine Ueberschrift vorausgehen und aber auch keine Leerzeile
        if ( not ( string.sub( spalten[self.nr][#spalten[self.nr]], 1,2) == "--" ) ) and
           ( not ( string.gsub(spalten[self.nr][#spalten[self.nr]]," ","") == "" ) ) then
            if self.summe < 9.99 then
                table.insert(spalten[self.nr],'        ------')
            elseif self.summe < 99.99 then
                table.insert(spalten[self.nr],'       -------')
            elseif self.summe < 999.99 then
                table.insert(spalten[self.nr],'      --------')
            else
                table.insert(spalten[self.nr],'     ---------')
            end
            table.insert(spalten[self.nr],'  '..string.format('%10.2f',self.summe):gsub('%.',',') .. ' ' .. CURRENTSHORT)
        end
    end
    
    if #spalten[self.nr] > 1 then
        table.insert(spalten[self.nr],'     ')
    end

end

--**********************************************************************

function App.fehltage_eingetragen (self,reports,spalten,dformat)

    local datum_midnight_format  = string.sub(self.datum_raw,1,8) .. "000001"
    local datum_midnight         = self:time(datum_midnight_format)
    datum_midnight_format        = self:date(t("%y%m%d%H%M%S"),datum_midnight)

--    print("==1===",self:print_heartbeat())

    local datum0_midnight_format = string.sub(self.datum0_raw,1,8) .. "000001"
    local datum0_midnight        = self:time(datum0_midnight_format)

    if datum0_midnight > 0 and datum_midnight - datum0_midnight > 86400 then

        if datum_midnight - datum0_midnight > 2*86400 then
            table.insert(spalten[self.nr], self:date(dformat,datum0_midnight+86400) .. " -")
        end

        self.datum1 = self:date(dformat,datum_midnight-86400)
        table.insert(spalten[self.nr],self.datum1)
        table.insert(spalten[self.nr],'--========')  --  dies zeigt an, dass das Geraet wirklich in diesen Tagen nicht gelaufen ist
        table.insert(spalten[self.nr],' ') 
--        self:rewrite_to_reports(reports)    -- die Zeile in diesem Falle zurueckschreiben in die newreport
--        self.datum0     = self.datum
--        self.datum0_raw = self:date("%y%m%d%H%M%S",datum_midnight-86400)

--        print("DDD",self.datum_raw,self.datum0_raw)

        if self.heartbeat_intervals[1] ~= "" and self.heartbeat_intervals[1] ~= string.sub(self.datum_raw,1,10) then 
            table.insert(self.heartbeat_intervals,1,"")  --  weil es ja mindestens einen ganzen Fehltag gibt, muss ein neues
        end                                              --  Intervall angefordert werden

--    print("==2===",self:print_heartbeat())

        return(1)

    end
    
    return(0)


end

--**********************************************************************

function App.rewrite_to_reports (self,reports,has_no_act_datum_line,alle_report_zeilen_untergebracht)

    if #self.heartbeat_intervals > 1 and self.heartbeat_intervals[1] == "" then
        table.remove(self.heartbeat_intervals,1) 
    end
    
    self.debug1:write(tostring(has_no_act_datum_line)..tostring(alle_report_zeilen_untergebracht)..self.zeile)
    
    if alle_report_zeilen_untergebracht == 0 then
        table.insert(reports,1,self.zeile)
    end
    if self.current ~= "." then
        if has_no_act_datum_line == nil then
            table.remove(self.zeilen_for_shortreport,1)
        end
    end

end


--**********************************************************************

function App.underline_nachgetragen (self,spalten)


    for i = 1,2,1 do
        if spalten[i] ~= nil and #(spalten[i]) >= self.last_underline then
            if spalten[i][self.last_underline] == "DDD" then
                local datum_underline = "--"
                local zaehler = 0
                local stunden = self.heartbeats
                while zaehler < 24 do
                    zaehler         = zaehler + 3
--                    for i,z in ipairs(self.heartbeats) do print("UUU",z) end
--                    print("QQU",stunden[zaehler-2],2*stunden[zaehler-1],4*stunden[zaehler],zaehler,1+stunden[zaehler-2]+2*stunden[zaehler-1]+4*stunden[zaehler])
                    datum_underline = datum_underline .. self.symbols_for_underlining[1+stunden[zaehler-2]+2*stunden[zaehler-1]+4*stunden[zaehler]]
                end
          --    datum_underline = "----------"   --  zu Testzwecken das detaillierte Underlining ausschalten
                spalten[i][self.last_underline] = datum_underline
                return
            end
        end
    end
    
    for i,zeile in ipairs(spalten[2]) do   --  falls sich die Angabe last_underline wegen rearrange_spalten verschoben hat
        if zeile == "DDD" then
            self.last_underline = i
            self:underline_nachgetragen(spalten)
            break
        end
    end
    
    
end
   
--**********************************************************************

function App.datum_eingetragen (self,spalten)

--    datum_underline = "----------"   --  zu Testzwecken das detaillierte Underlinig ausschalten
                     
    if self.only_heartbeat == 0 or datum_underline ~= "----------" then  --  nur Datumsueberschrift machen, wenn es mehr als Heartbeat-Eintraege gibt, oder Heartbeats fehlen
        table.insert(spalten[self.nr],self.datum)      -- jetzt die Datumsueberschrift
        table.insert(spalten[self.nr],"DDD")   -- und das Underlining, welches gleichzeitig Heartbeat-Luecken zeigt, das kann aber erst im Nachhinein eingetragen werden
        self.last_underline = #(spalten[self.nr])
    end
        
end


--**********************************************************************

function App.new_report_line (self,spalten)

    self.betrag = tonumber(self.betrag)
    self.summe  = self.summe + self.betrag
    self.total  = self.total + self.betrag
    if self.betrag < 0.00000001 then
        self.betrag = "   " .. trim(self.betrag_remark)
    else
        self.betrag = string.format('%7.2f',self.betrag)
        self.betrag = self.betrag:gsub('%.',',')
    end
    if self.current ~= "." then    --  bei current = "." handelt es sich nur um einen Heartbeat-Eintrag, also nicht beruecksichtigen
        table.insert(spalten[self.nr],self.uhrzeit..self.betrag..' '..self.current)   --  hier wird die Ticket-Zeile eingetragen
        contains_real_entries = 1
    end

end

--**********************************************************************

function App.add_netto_ust_total (self,spalten,max_zeilen)

    local umsatzsteuersatz = fcheck(0,tonumber,self.tax_vat:read())
    if umsatzsteuersatz == nil then
        umsatzsteuersatz = 0
    end

    umsatzsteuersatz = self:sync_report_data("tax_vat",umsatzsteuersatz)

    local usteuer = umsatzsteuersatz / ( 100 + umsatzsteuersatz ) * self.total
    local netto   = 100 / ( 100 + umsatzsteuersatz ) * self.total
    if self.nr == 1 and #spalten[1] > max_zeilen - 9 or #spalten[2] < 2 then
        self.nr = 2
    end
    if #spalten[self.nr] > 0 then
        if #string.gsub(spalten[self.nr][#spalten[self.nr]],' ','') > 0 then
            table.insert(spalten[self.nr],'')
        end
    end

    umsatzsteuersatz = (umsatzsteuersatz * 1000 + 1)/100
    if math.floor(umsatzsteuersatz % 10) == 0 then
        umsatzsteuersatz = string.format('%2u',math.floor(umsatzsteuersatz/10))
    else
        umsatzsteuersatz = trim(string.format('%4.1f',umsatzsteuersatz/10))
        umsatzsteuersatz = umsatzsteuersatz:gsub('%.',',')
    end

    local o1 = string.format('%9.2f',netto):gsub('%.',',')
    table.insert(spalten[self.nr],"NETTO"..o1:gsub(" ",":",1).."")
    if #umsatzsteuersatz == 2 then
        o1 = string.format('%9.2f',usteuer):gsub('%.',',',1)
        table.insert(spalten[self.nr],t('UST',tn)..umsatzsteuersatz..o1:gsub(" ","%%",1).."" )
    elseif usteuer < 1000.00 then
        table.insert(spalten[self.nr],t('UST',tn)..umsatzsteuersatz..'%' .. string.format('%6.2f',usteuer):gsub('%.',','))
    else
        table.insert(spalten[self.nr],t('UST',tn)..umsatzsteuersatz      .. string.format('%7.2f',usteuer):gsub('%.',','))
    end
    o1 = string.format('%9.2f',self.total):gsub('%.',',',1)
    table.insert(spalten[self.nr],'TOTAL'..o1:gsub(" ",":",1).."")
        
end


--**********************************************************************

function App.make_signa_report (self,text1,text2,reportnr)

    local text = ''                  

    if text2 == "" then   -- fuer Tests
        text = text1
    else
        for i,zeile in ipairs(text1) do
            text = text .. zeile
        end
        for i,zeile in ipairs(text2) do
            text = text .. zeile
        end
        text = text:gsub(' ','')  -- dies ist der Text des Reports, der zum Signieren verwendet wird
    end    


--    print("SIGNTEXT",text)

    local md5_report    = ( md5lib.hash(text:gsub(' ','')) )
    error2("short0001")
    local md5_crypt_hex = self:crypt(sec_key, md5_report)
    error2("short0002")
    local md5_crypt_bin = bin.hextos( md5_crypt_hex )
--    print(md5_crypt_bin)
    error2("short0003")
    self.signature      = base27_encode( md5_crypt_bin )
--    print("SIG",self.signature,#self.signature)
    local signa_short   = string.sub(md5_crypt_hex,2,3) .. string.sub(md5_crypt_hex,10,11) .. string.sub(md5_crypt_hex,16,17) 
    error2("short0004")
--    print("Short",signa_short)
    return(signa_short)
    
end

--**********************************************************************

function App.tagesnr (self,aktdate)

    return( math.floor ( aktdate / 86400 ) )
    
end

--**********************************************************************

function App.make_short_report (self)

    local text1        = ""
    local alfa         = "abcdefghijklmnopqrstuvwxyz"
    
    local loc1         = bin.stohex( self.location1:read() )
    local whoadm       = bin.stohex( self.report_data['whoadmin'] )
    local reportstatic = string.format("%02x",self.report_data['lang']) .. string.format("%04x",10*tonumber(self.report_data['tax_vat'])) .. loc1 .. whoadm
    local reporthash   = self:hash_from_reportstatic(reportstatic)
    if not string.match(self.reporthashes,","..reporthash..",") then   --  der Hash existiert schon, d.h. es gibt schon einen QR-Export, der die Daten enthaelt
        self.reporthashes0 = self.reporthashes
        self.reporthashes  = self.reporthashes .. reporthash .. ","     
        reporthash = "f0" .. string.format("%02x",#loc1/2) .. string.format("%02x",#whoadm/2)  --  dieser Dummy Reporthash gibt Laengen an,  > 8fffff
    else
        reportstatic = ''  --  diese Information wird ja nicht mehr gebraucht  (reporthash <= 8fffff)
    end

    text1 = text1 .. string.format("%010x",tonumber(self.report_data["reportdatum"]))  --  5 Byte
    text1 = text1 .. string.format("%04x", tonumber(self.report_data["reportnr"])) --  2 Byte
    text1 = text1 .. reporthash    --  3 Byte Hash der uebrigen statischen Felder
    text1 = text1 .. reportstatic  --  falls der String noch nicht existiert, hier explizit angeben
    text1 = text1 .. self.report_data["signastamp"] --  3 Byte
    text1 = text1 .. string.format("%02x",self.report_data["maxzeilen"]%256)  --  1 Byte

--    print("TTT",#text1,text1)


    local last_day = self:tagesnr( tonumber(self.report_data["reportdatum"]) - 1600000000 )
     
    local z1 = 0
    while 0 == 0 do
        z1 = z1 + 1
        if z1 % 10 == 0 then
            self:interrupt_report_computation("Loop for short report, WAIT26")
        end
            error2("shortreport0001")

    -- 1. Byte:
        -- Bit 1: 0 Betrags-Eintrag, 1 Alt Mode
        -- Bit 2: mit Zusatzbyte f Betrag (bei Alt Mode: 0: Heartbeat Intervallangabe, 1: Kuerzel-Eintrag bzw Short Betrag)
        -- Bit 3: mit Zusatzbyte(s) f Zeit (die ein oder zwei Bytes sind eine Addition von Tagen)
        -- Bit 4/5:  Bei Alt Mode und Kuerzel:   die ersten zwei Bits des Kuerzel-Eintrags,
        --           Bei Alt Mode und Heartbeat: zusammen mit Bit 6-8 Angabe der Tagesstunde
        --           Bei Normal Mode:            00: E, 01: Q
        -- Bit 6-8: ersten drei Bits für Angabe der Minute des Tages. 
    -- 2. Byte: Zeit (weitere 8 Bits, also insgesamt 11 Bits fuer Minute des Tages)
    -- 3. Byte: Betrag (bzw. bei Alt Mode die naechsten 8 Bit des Kuerzel-Eintrags)
    -- 4.-6. Byte: Zusatzbyte Betrag plus Zusatzbytes Zeit

        local zeile1  = self.heartbeat_intervals[ #self.heartbeat_intervals ]
--        if zeile1 == "" then
--            zeile1 = nil
--        end
        if zeile1 ~= nil then
            zeile1 = zeile1 .. "0005"
        end
        local zeile2  = self.zeilen_for_shortreport[ #self.zeilen_for_shortreport ]
        local current
        local betrag
        local remark
        if zeile2 ~= nil then
            current = zeile2[2]
            betrag  = zeile2[3]
            remark  = zeile2[4]
            zeile2  = zeile2[1]
        end
        local zeile

    error2("shortreport1001")
 
       if zeile1 == nil then
            if zeile2 == nil then
                break
            else
                zeile = zeile2
                table.remove(self.zeilen_for_shortreport)
            end
        else
            if zeile2 == nil then
                zeile   = zeile1
                current = "."
                table.remove(self.heartbeat_intervals)
            else
                if tonumber(zeile1) < tonumber(zeile2) then
                    zeile   = zeile1
                    current = "."
                    table.remove(self.heartbeat_intervals)
                else
                    zeile = zeile2
                    table.remove(self.zeilen_for_shortreport)
                end
            end
        end
--        print(zeile,zeile1,zeile2,#self.zeilen_for_shortreport)

        local byte1       = 0

        local akt_day     = self:tagesnr( self:time(string.sub(zeile,1,14)) )

    
        local time_diff   = akt_day - last_day
        last_day          = akt_day
                
        local tagesminute = 60 * tonumber(string.sub(zeile,9,10)) + tonumber(string.sub(zeile,11,12))
        local byteminute  = string.format("%03x",tagesminute)
        local byte2       = string.sub(byteminute,2,3)
        
        local betragbyte1 = ""
        local betragbyte2 = ""
        local zeitbyte1   = ""
        local zeitbyte2   = ""

    error2("shortreport1002")

        if time_diff > 0 then   --  Additionstage muessen angegeben werden

            byte1     = byte1 + 1*32  --  3. Bit zeigt an, dass es Additionstage gibt
            zeitbyte1 = time_diff % 128
            zeitbyte2 = math.floor(time_diff/128)

            if zeitbyte2 > 0 then
                zeitbyte1 = zeitbyte1 + 1*128 --  erstes Bit sagt, dass noch ein weiteres Bit kommt
                zeitbyte2 = string.format("%02x",zeitbyte2)
            else
                zeitbyte2 = ""
            end
            zeitbyte1 = string.format("%02x",zeitbyte1)

        end
        
    error2("shortreport1003")
        if current == "." then    --  heartbeat interval. Hier werden die Stunden angegeben, Minuten braucht man nicht

            byte1             = byte1 + 1*128        --  Alt Mode
            byte1             = byte1 + 0*64         --  ist Heartbeat-Intervallangabe
            local tagesstunde = tonumber(string.sub(zeile,9,10))
--            print("TAGESSTUNDE",tagesstunde)
            byte1             = byte1 + tagesstunde
--            print("BBB",byte1)
            byte2             = ""
            
        else

            byte1 = byte1 + tonumber("0x"..string.sub(byteminute,1,1))
            
            if current == "X" then

                byte1 = byte1 + 1*128        --  Alt Mode
                byte1 = byte1 + 1*64         --  Kuerzel Eintrag
--                print("BBB",remark,self.zeile)
                remark = string.sub(remark,#remark-1,#remark)
                if string.find(remark,"%D") == nil then  --  wenn doch ein Betrag angegeben ist
                    remark = 900 + tonumber(remark) --  um das von einer Buchstaben-Kodierung zu unterscheiden
                else   --  wenn ein Buchstaben Kuezel gegeben ist
                    remark = (string.find(alfa,string.sub(remark,1,1))-1) * 26 + (string.find(alfa,string.sub(remark,2,2))-1)
                end
                local bytebetrag = string.format("%03x",remark)
--                print("RRR",remark,bytebetrag,byte1,string.format("%0x",byte1))
                byte1            = byte1 + tonumber(string.sub(bytebetrag,1,1)) * 8  --  die ersten zwei Bits des zehnstelligen Kuerzel-Betrags-Bytes
                betragbyte1      = string.sub(bytebetrag,2,3)
--                print(bytebetrag,betragbyte1,string.format("%0x",byte1))

            else
            
                if current == "Q" then
                    byte1 = byte1 + 1*8
                else
                    byte1 = byte1 + 0*8
                end
                betrag      = math.floor(betrag * 100 / self.ggt + 0.0001)
                betragbyte1 = betrag % 256
                betragbyte2 = math.floor(betrag/256)
                if betragbyte2 > 0 then
                    byte1       = byte1 + 1*64  -- zeigt an, dass es ein Zusatzbyte fuer den Betrag gibt
                    betragbyte2 = string.format("%02x",betragbyte2)
                else
                    betragbyte2 = ""
                end
--                print(self.ggt,betrag,betragbyte1,betragbyte2)
                betragbyte1 = string.format("%02x",betragbyte1)
                
            end
            
        end

    error2("shortreport1004")

        
        byte1 = string.format("%02x",byte1)

--        print("ENTRY",zeile,current,betrag,"TAG",akt_day,time_diff,"---",byte1,byte2,".",betragbyte1,betragbyte2,zeitbyte1,zeitbyte2)

        text1 = text1 .. byte1 .. byte2 .. betragbyte1 .. betragbyte2 .. zeitbyte1 .. zeitbyte2

    end
 
--    print("WWER",#text1,text1)

    error2("shortreport0001a")
    local text1_bin       = bin.hextos( text1 )
    error2("shortreport0001b")
    local text1_crypt     = self:crypt(sec_key, text1_bin)
    error2("shortreport0001c")
    self:interrupt_report_computation("Loop for short report, WAIT26")
    error2("shortreport0002")
    local text1_crypt_bin = bin.hextos( text1_crypt )
    self:interrupt_report_computation("Loop for short report, WAIT26")
    error2("shortreport0003")
    local text1_crypt_enc = base62_encode( text1_crypt_bin )
    self:interrupt_report_computation("Loop for short report, WAIT26")
    error2("shortreport0004")
    local text1_cry32_enc = base32_encode( text1_crypt_bin )
    
--    print(text1_cry32_enc)
    
    local sr32 = {}
    while text1_cry32_enc ~= '' do
        local t1        = string.sub(text1_cry32_enc,1,24)
        text1_cry32_enc = string.sub(text1_cry32_enc,25)
        local nr1       = 1 + #sr32 % 32   --  Zeilen Zaehler
        t1              = t1 .. string.sub(CHAR32,nr1,nr1)
        local t2        = bin.stohex( md5lib.hash(t1) )
        t2              = tonumber("0x"..string.sub(t2,1,3)) % 1000
        local nr2       = 1 + math.floor(t2/32)
        local nr3       = 1 + t2 % 32
        table.insert(sr32, string.sub(
                           string.sub(t1,1,5)   .. " " .. string.sub(t1,6,10) .. " " .. string.sub(t1,11,15) .. " " ..
                           string.sub(t1,16,20) .. " " .. string.sub(t1,21,25) ..
                           "                                           ",1,29)  .. " " ..
                           string.sub(CHAR32,nr2,nr2) .. string.sub(CHAR32,nr3,nr3) )
    end        

    error2("shortreport0005")

    local text1_crypt_enc_with_sno_class = string.sub(text1_crypt_enc,1,10) .. SE_CLASS ..  string.sub(text1_crypt_enc,11) 

    return text1_crypt_enc_with_sno_class,sr32
    
end

--**********************************************************************

function App.hash_from_reportstatic (self,reportstatic)

    local reporthash  = bin.stohex( md5lib.hash(reportstatic) )
    reporthash        = string.format("%02x", tonumber( "0x" .. string.sub(reporthash,1,2) ) % 128 ) .. string.sub(reporthash,3,6)
    return(reporthash)

end

--**********************************************************************

function App.unfold_report (self,text2,report_data)   --  stellt erstmal eine dummy newreport-Datei her, die danach als Report formatiert werden kann

    if SE_CLASS ~= string.sub(text2,11,12) then
        return("---Wrong_Serialnumber_class---")
    end

    local text1_crypt_enc = string.sub(text2,1,10) .. string.sub(text2,13)
    local text1_crypt_bin = base62_decode( text1_crypt_enc )
    local text1_crypt     = bin.stohex( text1_crypt_bin )

    local text1_bin       = self:decrypt(pub_key, text1_crypt )
    local text1           = bin.stohex( text1_bin )
    local alfa            = "abcdefghijklmnopqrstuvwxyz"

    if #text1 < 10 then
        return("---No_valid_transmit_text---")
    end

    report_data["reportdatum"]  = tonumber("0x"..string.sub(text1,1,10))
    report_data["reportnr"]     = string.format("%1u",tonumber("0x"..string.sub(text1,11,14)))

    local reporthash            = string.sub(text1,15,20)
    local rdata                 = {}
    if string.sub(reporthash,1,1) == "f" then   --   Statische Daten werden mitgegeben
        local loc1_len     = tonumber("0x" .. string.sub(text1,17,18))
        local whoadm_len   = tonumber("0x" .. string.sub(text1,19,20))
        local reportstatic = string.sub( text1,21,27+2*loc1_len+2*whoadm_len )
        local reporthash   = self:hash_from_reportstatic(reportstatic)
        text1              = string.sub( text1,27+2*loc1_len+2*whoadm_len )
        rdata["lang"]      = tonumber("0x" .. string.sub(reportstatic,1,2))
        rdata["tax_vat"]   = string.format("%3.1f", tonumber("0x" .. string.sub(reportstatic,3,6)) / 10 )
        rdata["location"]  = bin.hextos( string.sub(reportstatic,7,7+2*loc1_len-1) )
        rdata["whoadmin"]  = bin.hextos( string.sub(reportstatic,7+2*loc1_len,7+2*loc1_len+2*whoadm_len-1) )
    else
        rdata = self.static_strings[reporthash]
        if rdata == nil then
            return("---No_static_data---")
        end
        text1 = string.sub(text1,21)
    end
    
    report_data["signastamp"]  = string.sub(text1,1,6)
    report_data["maxzeilen"]   = string.format("%010u",tonumber("0x"..string.sub(text1,7,8)))
    text1 = string.sub(text1,9)
    
    local tagesdatum    = tonumber(report_data["reportdatum"]) - 1600000000
    tagesdatum          = 86400 * math.floor(tagesdatum/86400) + 1 
    local newreport     = {}
    local heartbeats    = {}
    local entry_hours   = {} -- diese Stunden brauchen nicht durch Heartbeats abgedeckt zu werden, weil reale Entries vorliegen

    
    while 0 == 0 do   --  ab hier alle Eintraege in der text1 evaluieren
    
--        print("TT",string.sub(text1,1,2))
        local byte1        = bin.hextobin( string.sub(text1,1,2) )
        local betrag       = ""
        local betragnr     = 0
        local kuerzel      = ""
        local add_tag      = 0 -- Tagesdifferenz
        local is_neartbeat = 0
        local current      = CURRENTSHORT

        if byte1[5] == "1" then
            current = "Q"
        end

        local tagesminute
        if byte1[1] == "0" or byte1[2] == "1"  then   --  Normal mode oder Alt mode mit Kuerzel Angabe
            betrag      = string.sub(text1,5,6)
            tagesminute = tonumber(byte1[6])*1024 + tonumber(byte1[7])*512 + tonumber(byte1[8])*256 + tonumber("0x"..string.sub(text1,3,4))
            text1       = string.sub(text1,7)
        else    --   das ist dann ein Heartbeat-Eintrag, d.h. nur ein einziges Byte plus evtl. Zeit-Byte(s)
            tagesminute  = tonumber(byte1[4])*16 + tonumber(byte1[5])*8 + tonumber(byte1[6])*4 + tonumber(byte1[7])*2 + tonumber(byte1[8])
--            print("HBB",tagesminute)
            tagesminute  = tagesminute * 60 + 5
            is_heartbeat = 1
            current      = "."
            text1        = string.sub(text1,3)    --   das ist dann ein Heartbeat-Eintrag, also Haupteintrag nur ein einziges Byte
        end
                
        if byte1[2] == "1" then 
            if byte1[1] == "0" then  --  Normal-Mode: ein Zusatzbyte f Betrag noetig
                betrag = betrag .. string.sub(text1,1,2)
                text1  = string.sub(text1,3)
            else  --  Alt-Mode: Kuerzel-Eintrag
                local kuerzelnr = tonumber(byte1[4])*512 + tonumber(byte1[5])*256 + tonumber("0x"..betrag)
                if kuerzelnr > 900 - 1 then
                    betragnr = kuerzelnr - 900
                else
                    local n1 = 1 + math.floor(kuerzelnr/26)
                    local n2 = 1 + kuerzelnr%26
                    kuerzel  = string.sub(alfa,n1,n1) .. string.sub(alfa,n2,n2)
--                    print("KNK",byte1[4],byte1[5],betrag,kuerzelnr,n1,n2,kuerzel)
                end
                current  = "X"
                betrag   = ""
            end
        end
        
        if byte1[3] == "1" then  --  es gibt einen Wechsel des Tages
            add_tag = tonumber( "0x" .. string.sub(text1,1,2) )
            if add_tag > 127 then
                add_tag = (add_tag - 128) + 128 * tonumber( "0x" .. string.sub(text1,3,4) )
                text1   = string.sub(text1,5)
            else
                text1   = string.sub(text1,3)
            end
        end
        
        tagesdatum            = tagesdatum + 86400*add_tag
        local stdmin          = string.format("%02u",math.floor(tagesminute/60)) .. string.format("%02u",tagesminute%60)
        local datum_formatted = self:date("%y%m%d",tagesdatum) .. stdmin .. "10"
        
        if betrag ~= "" then
            betrag = string.format("%6.2f",self.ggt*tonumber("0x"..betrag)/100+0.0001)
        elseif kuerzel ~= "" then
            betrag = "  0," .. kuerzel
        else
            betrag = string.format("%6.2f",betragnr/100+0.0001)
        end
        betrag = string.gsub(betrag,"%.",",")
        
        if current == "." then
--            print("HBC",tagesdatum,tagesminute)
            table.insert(heartbeats,tagesdatum+tagesminute*60+10)
        else
            table.insert(newreport,datum_formatted..current .. "  " .. betrag)
            entry_hours[string.sub(datum_formatted,1,10)] = 1
        end
        
        
        if current ~= "" then
            print(#text1,tagesminute,datum_formatted..current,betrag)
        end
        if #text1 == 0 then
            break
        end
        
    end

    print("-----")
    
    local heartbeats_expanded       = self:expand_heartbeats(heartbeats,entry_hours)
--    for i,z in ipairs(heartbeats_expanded) do print("ddd",z) end

    local newreport_with_heartbeats = self:merge_heartbeats_to_newreport(heartbeats_expanded,newreport)

--    print("NN",newreport_with_heartbeats)

    return(newreport_with_heartbeats)

end


--**********************************************************************

function App.recreate_report(self,short_report)   --  aus einem short_report wieder den Report rekonstruieren
                                                  --  diese Funktion wird nicht im Geraet eingesetzt
    MAXTRANSMIT0     = MAXTRANSMIT
    MAXTRANSMIT      = 400
    self.report_data = {}
    local xx         = self:unfold_report(self.sr,self.report_data)
    self.alt_report  = "xxx\n---\n" ..xx

    self.report_data['reports']    = nil
    self.report_data['signastamp'] = nil
    
    self.squeeze_out = 1
    local filename1  = xx
    if string.sub(xx,1,3) ~= "---" then
        filename1 = self:p_generate_report()
        print("-----------")
    end

    MAXTRANSMIT     = MAXTRANSMIT0
    self.alt_report = nil
    
    return(filename1)

end


--**********************************************************************

function App.expand_heartbeats (self,heartbeats,entry_hours)

    local heartbeats_expanded = {}
    
    while 0 == 0 do
        
        local a1 = table.remove(heartbeats,1)
        if a1 == nil then
            break
        end
        local a2 = table.remove(heartbeats,1)
--        print("HBEX",a1,a2)

        while 0 == 0 do
            if a1 > a2 then
                break
            end
            local a1_formatted = self:date("%y%m%d%H%M%S",a1)
--            print(">>",a1_formatted)
            if entry_hours[string.sub(a1_formatted,1,10)] == nil then
                table.insert(heartbeats_expanded,a1_formatted..".    0,00")
            end
            a1 = a1 + 3600
--            print(a1,a2)
        end
        
    end
    
    return(heartbeats_expanded)

end

--**********************************************************************

function App.merge_heartbeats_to_newreport (self,heartbeats_expanded,newreport)

    local newreport1 = {}
    
    while 0 == 0 do

        local zeile1 = nil
        local zeile2 = nil
        
        if #heartbeats_expanded > 0 then
            zeile1 = heartbeats_expanded[#heartbeats_expanded]
        end
        
        if #newreport > 0 then
            zeile2 = newreport[#newreport]
        end

        if zeile1 == nil then
            if zeile2 == nil then
                break
            else
                table.insert(newreport1,1,table.remove(newreport))
            end
        else
            if zeile2 == nil then
                table.insert(newreport1,1,table.remove(heartbeats_expanded))
            else
--                print(zeile1,zeile2)
                if tonumber(string.sub(zeile1,1,14)) < tonumber(string.sub(zeile2,1,14)) then
                    table.insert(newreport1,1,table.remove(newreport))
                else
                    table.insert(newreport1,1,table.remove(heartbeats_expanded))
                end
            end
        end    
    end
    
    newreport2 = ""
    for i,z in ipairs(newreport1) do newreport2 = newreport2 .. z .. "\n"  end

    return(newreport2)

end

--**********************************************************************

function App.read_qr_reports (self)


end

--**********************************************************************

function App.test1_generate_report (self,opt_par)


--    self.alt_report = ""
    local text2 = {}
    
    local rs = 1
    if file_open ~= nil then
        rs = tonumber(self:time())*1000+tonumber(get_timer_value(29))
    else
        rs = math.floor ( (self:time()*10000+os.clock()*1000000000000+tonumber(3434))  % 9999 )
    end

--    rs = 2141

    
    math.randomseed(rs)
    print("RANDOMSEED",rs)
    local zaehler = 0
    zaehler = 1
    st      = 1731866900 + tonumber(3434)
    while zaehler < 110 do
        st = st + math.random(1000,5000)
        if math.random(1,10) == 1 then
            st = st + math.random(1000000,5000000)
        end
        cu = math.random(1,10)
        cu = string.sub(CURRENTSHORT.."QX"..CURRENTSHORT..CURRENTSHORT..CURRENTSHORT.."....",cu,cu)
        if cu == "." then
            betrag = " 0,00"
        elseif cu == "X" then
            betrag = ({"0,as","0,ph","0,tx","0,fg"})[math.random(1,4)]
        else
            betrag    = string.gsub(string.format("%5.2f",math.random(2,90)/10),"%.",",")
        end
        local zeile1 = self:date("%y%m%d%H%M%S",st) .. cu .. " "  .. betrag
        table.insert(text2,zeile1)
--        self.alt_report = self.alt_report .. self:date("%y%m%d%H%M%S",st) .. cu .. " "  .. betrag .. "\n"
        zaehler = zaehler + 1
    end
    
--    print("AAS",self.alt_report)

    self.report_is_integer  = 1


    text20  = {}
    text202 = [[20241117194138.  0,00
20241117204212M  4,20
20241117212319.  0,00
20241117220121.  0,00
20241117222708M  4,60
20241117233736X  0,fg
20241118005234.  0,00
20241118011002M  6,90
20241118021503M  0,30
20241118025446.  0,00
20241118041657Q  3,30
20241118053309.  0,00
20241118063230.  0,00
20241118075338M  7,00
20241118083034.  0,00
20241230192821Q  2,30
20241230200201.  0,00
20250225224203M  7,00
20250225234232.  0,00
20250226005200.  0,00
20250226015934M  5,10
20250226022506.  0,00
20250226031049M  0,40
20250226043012M  5,60
20250226051135.  0,00
20250226054915.  0,00
20250226062816Q  1,10
20250226074638M  6,50
20250226081145M  4,20
20250406013051.  0,00
20250419150302.  0,00
20250419152302.  0,00
20250419154818X  0,fg
20250419163509.  0,00
20250419171121M  4,20
20250419173041.  0,00
20250419175342M  9,00
20250419181845M  1,60
20250419184138.  0,00
20250419190557.  0,00
20250419193336M  2,80
20250419204343.  0,00
20250419211720M  5,00
20250419215624M  3,50
20250419224013M  0,60
20250420000244Q  4,60
20250420011433M  0,30
20250420014602X  0,fg
20250420030034M  1,60
20250420033418.  0,00
20250420042500M  0,70
20250420054243M  2,70
20250420061718.  0,00
20250420065522X  0,ph
20250609133946M  0,70
20250609140016M  8,10
20250609145927X  0,fg
20250609151724.  0,00
20250625051647M  8,50
20250625063636X  0,as
20250625073622M  5,80
20250711215922X  0,fg
20250711222740.  0,00
20250711234717.  0,00
20250712004244M  3,50
20250712012736M  8,60
20250712022049M  4,40
20250712024522.  0,00
20250712032419.  0,00
20250712043904.  0,00
20250712055615Q  7,10
20250712070258.  0,00
20250712080558M  2,60
20250712085938.  0,00
20250712102131.  0,00
20250712114015.  0,00
20250712122822M  6,10
20250712134446M  5,40
20250712141720M  1,50
20250712151023.  0,00
20250712154649.  0,00
20250712170935Q  2,50
20250712182625.  0,00
20250712194147.  0,00
20250712205233M  2,70
20250712220531.  0,00
20250712231133M  3,10
20250712234305.  0,00
20250713002423M  2,80
20250713005823.  0,00
20250729080222M  8,80
20250729091858.  0,00
20250729103227.  0,00
20250729112305.  0,00
20250729120321.  0,00
20250729122618M  4,00
20250729131759X  0,fg
20250729142250Q  8,90
20250729151409X  0,tx
20250729154536M  1,20
20250729161534.  0,00
20250901144553M  0,40
20250901155819.  0,00
20250901171703X  0,ph
20251012112742M  6,80
20251012121249.  0,00
20251012133236.  0,00
20251012142501.  0,00
20251012144815.  0,00]]

    
    for zeile in string.gmatch(text202,'[^\n]+') do
        table.insert(text20,zeile)
    end
    
--    text2 = text20
    

    if  opt_par ~= nil then
    
--        local text1_crypt     = self:crypt(sec_key, self.alt_report)
--        local text1_crypt_bin = bin.hextos( text1_crypt )
--        local text1_crypt_enc = base62.encode( string.sub(text1_crypt_bin,1,2000) )

        print(text2[1])
        self:write_report(text2)

    else
    
        self.lang_nr_for_report = 1
        
        self.alt_report = "xxx\n---\n"   --   zufaellige  newreport-daten
        for i,zeile in ipairs(text2) do
            self.alt_report = self.alt_report .. zeile .. "\n"
        end

        local filename = self:p_generate_report()
        os.execute("lua main.lua read "..filename)   --  daraus File und shortreport machen
        
--        print(self.sr)

        local filename1 = self:recreate_report(self.sr)   --- und gleich wieder zurueckrechnen
        print("-----------"..filename1)
--        if string.sub(filename1,1,3) ~= "---" then
            os.execute("lua main.lua read "..filename1)
--        else
--            print("No file generated. "..filename1.."\n")
--        end


    end

end


--**********************************************************************

function App.test1_print_big_qrcode (self,opt_par,nr)

    local test3302 = {

'QQx1b@',
'QQx1d(k40QQx31QQx41QQx31QQx00',
'QQx1d(k30QQx31QQx43QQx04',
'QQx1d(k30QQx31QQx45QQx34',
'QQx1d(k30QQx31QQx51QQx30',
'QQx1d(kQQxe8QQx00QQx31QQx50QQx30',
'QQhttps://cx.12park.de/RRR00OPp30SE2MS1Q02V003VKv129462379463791462T88ZsHLqOl3z1',
'rAKmc27UGeZjqPuqkLKiIwBFVd9YO928mB7MP4meptdbbz9g9cDmp5cbC3vSUgFuY018',
'GostweQRZW01ePOSslKydxb17E6z8vvDdokM3PNy502x5qLq2ruNg',
'rU66gaoHAN00JOadQGAnMEn014f9hXa9imvB8y011J01gfdgfrgfdgfdsghhgfd02',
'QQx1d(k30QQx31QQx51QQx30',
'QW',
'QW',
' ',

}

    self:send_to_printer1(test3302)

end


--**********************************************************************

function App.test1_simu_taste (self)


--    print("112233 ------")

    if #self.tastenfolge == 1 then
    
        self:test1_new_tastenfolge()
        self:test1_simu_taste()
            
    elseif #self.tastenfolge ~= 0 then
        
        local factor    = 1
        local akt_pause = table.remove(self.tastenfolge,1)
        ACT_TASTE       = table.remove(self.tastenfolge,1)

        if self.simu_par == "03306" then
            if ACT_TASTE ~= 999999 then
                akt_pause = akt_pause / 10
            end
            if akt_pause > 60000 then
                akt_pause = 5500
            end
        end

        print(akt_pause,ACT_SCREEN,ACT_TASTE)
        --  error1("taste"..tostring(ACT_TASTE).." " ..tostring(akt_pause))

        start_timer(8,akt_pause/factor,0,1)

    end
        
end


--**********************************************************************

function App.test1_new_tastenfolge (self)


    local par = self.tastenfolge[1]

    if file_open ~= nil then
        rs = tonumber(self:time())*1000+tonumber(get_timer_value(29))
    else
        rs = math.floor ( (self:time()*10000+os.clock()*1000000000000+tonumber(3434))  % 9999 )
    end

--    rs = 2141

    
    math.randomseed(rs)


--    if SIMUDATA == nil then
--        local rr = my_read_filedata1('simudata.txt',sd_dir..'/simudata.txt')
--        if rr ~= nil then
--            rr = load(rr)
--            SIMUDATA = rr()
--        else
--            SIMUDATA = 0
--        end
--    end        

    SIMUDATA = nil


    if par == 101 then
    
        local zufallswerte = {2911,3482,4555,5723,2989,
--                              3322,4777,5922,6233,4771,
--                              4927,7432,4662,5371,5725,
--                              4927,7432,4662,5371,5725,
--                              4927,7432,4662,5371,5725,
                                         90002,3803}

        local zaehler    = 0
        local tt
        local mm
        self.tastenfolge = {}
        
        if SIMUDATA ~= nil and SIMUDATA ~= 0 then
            self.tastenfolge = table.remove(SIMUDATA,1)
            print("SD",SIMUDATA)
            if self.tastenfolge == nil then
                self.tastenfolge = {}
            end
        end

        if #self.tastenfolge == 0 then

            while zaehler < math.random(5,10) do
                zaehler = zaehler + 1
                table.insert(self.tastenfolge,math.random(800,4000))
                table.insert(self.tastenfolge,math.random(1081,1085))
            end
    
            self.tastenfolge[ 2 ]                 = math.random(1,3)
            self.tastenfolge[ #self.tastenfolge ] = 80
            table.insert(self.tastenfolge, zufallswerte [ math.random(1,#zufallswerte) ] )
            table.insert(self.tastenfolge, 999999)
            table.insert(self.tastenfolge, 101)

        end

        local atime = self:time()
        local dtime = string.sub(self:date("%y%m%d",atime),3,8) .. "_" .. self:date("%H%M%S",atime)

        local text1 = ''
        
        zaehler = 0
        for i,number in ipairs(self.tastenfolge) do
            zaehler = zaehler + 1
            text1 = text1 .. string.format("%9u",number)
            if zaehler % 2 == 0 then
                text1 = text1 .. "\n"
            else
                text1 = text1 .. "    "
            end
        end

        local readcount      = string.format("%04u",self:read_counter())
        local testdata_file  = "testdata__" .. dtime .. "__" .. readcount .. ".txt"
        my_write_filedata1(testdata_file,sd_dir..'/'..testdata_file,text1 .. "\n",over_write)
        local text2 = self:decrypt( pub_key3, my_read_filedata1('newreport.txt',sd_dir..'/newreport.txt') )
        local newreport_file = "newreport_" .. dtime .. ".txt"
        my_write_filedata1(newreport_file,sd_dir..'/'..newreport_file,text2 .. "\n",over_write)

    end

        
end


--**********************************************************************

function xxtranscode (b1,b2,r1,r2,s)

    erg = ""
    act_s = 0
    x   = 0
    
    while (0 == 0) do
    
        x = x*f1 + r1[string.sub(s,1,1)]
        s = string.sub(s,2)
        
        x0 = x
        while (0 == 0) do
            while (x0 > #b2) do
                x0 = x0 // #b1
            end
            if x0 > #b1 then
                
            end
        end
        
    end
    
end


--**********************************************************************

lensym  = 1

function App.qr_to_bitmap (self,faktor,qrdata,mode)   --  zeilenweises abarbeiten der qrcode-daten

    local erg     = {}
    local erg1    = {}
    
    local faktor1 = faktor
    local faktor2 = faktor
    
    local xreduce = 0
    local yreduce = 0
    local yloop   = 3
    
    if faktor1 % 2 == 0 then
        faktor1 = faktor1 / 2
        xreduce = 1
    end

    if faktor2 % 3 == 0 then
        faktor2 = faktor2 / 3
        yreduce = 1
        yloop   = 1
    end

    while (0 == 0) do

        feed_the_dog()
        if #erg*8/faktor2 > #qrdata then
            if mode == 9 then
                return erg,erg1
            else
                if xreduce == 0 then
                    if yreduce == 0 then  return erg,"21" end
                    if yreduce == 1 then  return erg,"01" end
                else
                    if yreduce == 0 then  return erg,"20" end
                    return erg,"00"
                end
            end
        end

        local zeile = ""
        local zeile1 = ""
        
        feed_the_dog()
        
        while (0 == 0) do
        
            feed_the_dog()
            if #zeile/faktor1 > lensym*yloop*#qrdata then
                break
            end

            for i1=1,yloop do            
                local dot = {nil,nil,nil,nil,nil,nil,nil,nil}
                for i = 1,8 do
                    local hdot = #erg*8*i1 + i
                    dot[i]     = qrdata[math.ceil(hdot/faktor2)]
                    if dot[i] ~= nil then                
                        dot[i] = dot[i][math.ceil(#zeile/lensym/faktor1)]
                    end
                    if dot[i] == nil or dot[i] < 0 then
                        dot[i] = 0
                    else
                        dot[i] = 1
                    end
                end
                local symbol = ( 16*(8*dot[1] + 4*dot[2] + 2*dot[3] + dot[4]) + (8*dot[5] + 4*dot[6] + 2*dot[7] + dot[8]) )
                if lensym == 1 then
                    zeile  = zeile .. string.char(symbol)
                else
                    zeile  = zeile .. "QQx" .. string.format("%02x",symbol)
                end
                if mode == 9 then
                    zeile1 = zeile1 .. tostring(dot[1]) .. tostring(dot[2]) .. tostring(dot[3]) .. tostring(dot[4]) .. tostring(dot[5]) .. tostring(dot[6]) .. tostring(dot[7]) .. tostring(dot[8])
                end
            end
        end
        
        table.insert(erg,zeile)
        
        if mode == 9 then

            for i=1,8 do
                table.insert(erg1,"")
                for j=1,#zeile1 do
                    erg1[#erg1] = erg1[#erg1] .. string.sub(zeile1,j*8+i,j*8+i)
                end
            end

        end
        
    end

end

--**********************************************************************

function App.reset_squeeze (self)

    self.squeeze_out = nil
    if file_open ~= nil then
        start_timer(24,1,0,1)
    end

end

--**********************************************************************

function App.set_lang_nr_for_report (self,actual_country0)

    local actual_country = actual_country0
    if actual_country0 == nil then
        actual_country = self.reportlang:read()
    end
    
    
    self.lang_nr_for_report = 999
    for i,country in ipairs(self.country_table) do
        if self.country_table[i] == actual_country then
            self.lang_nr_for_report = i
        end
    end
    
    if self.lang_nr_for_report == 999 then
        self:set_lang_nr_for_report( string.sub(self.lang_selector:read(),1,2))
    end
    
--    print("LLL",self.lang_selector:read(),self.reportlang:read())


--    return end function xxx1 ()


    if (string.sub(self.lang_selector:read(),1,2) == self.reportlang:read()) then
        self.reportlang1:set_visiable(0)
        self.reportlang:set_visiable(0)
    else
--        print("REPORT LANGUAGE:",actual_country,self.reportlang:read())
        set_text(1,200,"SN "..tostring(self.reportlang:read().." "..self.lang_selector:read().." "..tostring(actual_country)))
        if actual_country0 == actual_country then
            self.reportlang:write(actual_country)
        end
        self.reportlang1:set_visiable(1)
        self.reportlang:set_visiable(1)
    end

    
    
end

--**********************************************************************

function App.no_simulation (self)
    local kn1 = self
    local simulation = "no"
end


--**********************************************************************

function App.print_report (self)

    local text   = self:decrypt( pub_key3, my_read_filedata1('printbuffer.txt',sd_dir..'/printbuffer.txt') )
    print("PRESS-NUMBER", self.press_protocol)

    local prdata = {}
    for zeile in string.gmatch(text,'[^\n]+') do
        table.insert(prdata,zeile)
    end

    if #prdata < 2 and self.press_protocol > 1 then   --  hier der Fall, in dem der Printbuffer schon leer ist.
        print("WAIT-LOOP")
        WAIT_26                = 10  --  Intervall, in dem auf den naechsten Block der Signatur-Berechnung
        self.squeeze_out       = 1   --  gewartet wird.
--        self.press_protocol    = 1   --  Die aktuellen Daten des (auch ggfs halbfertigen) Reports werden gedruckt
        start_timer(25,10,0,1)
    else
--        if #prdata > 2 then     --  hier werden die Printbuffer-Daten ausgedruckt
            self.report_wird_gedruckt:set_visiable(1)
--        end
        print("QQQQ","DRUCK")
        self:send_to_printer(prdata,1)   --  loesche den Printbuffer erst, wenn alle Ticketreports ausgedruckt sind, deswegen ist die untere Zeile in die App.on_timer Funktion gewandert
--        my_write_filedata1('printbuffer.txt',sd_dir..'/printbuffer.txt',self:crypt( sec_key3,'                     ',80),over_write)
--             --   der lange Leerzeichen-String ist notwendig, weil sonst die Verschluesselung nicht funktioniert!

    end
        
end
    
--**********************************************************************

function App.pin_base (self,control)

    if control == 12 then  -- kommt vom adm_ext Screen
        self.pin_offset = 0
        self.pin_list   = {}
        self:update_list()
        self:pin_screen()
        return
    end
    
    if control == 99 then
        self.adm_ext:activate()

--    print ('PIN_BASE', control)
    elseif control == 12 then
        if self.pin_offset > 0 then
--            print('PIN',12,self.pin_offset)
            self.pin_offset = 0
            self:pin_screen()
        end
        
    elseif control == 14 then
        if self.pin_offset >= self.ANZAHL_PINS then 
--            print('PIN',14,self.pin_offset)
            self.pin_offset = self.pin_offset - self.ANZAHL_PINS
            self:pin_screen()
        end
        
    elseif control == 7 then
        if self.pin_offset + self.ANZAHL_PINS + 2 < #self.pin_list then
--            print('PIN',7,self.pin_offset)
            self.pin_offset = math.min(self.pin_offset + self.ANZAHL_PINS)
            self:pin_screen()
        end

    elseif control == 8 then
        if self.pin_offset - self.ANZAHL_PINS < #self.pin_list then
--            print('PIN',8,self.pin_offset)
            self.pin_offset = #self.pin_list - self.ANZAHL_PINS 
            self:pin_screen()
        end

    else
        control = control % 10
        if control == 0 then
            control = 10
        end
        local pin_defs  = self:read_pin_defs()
        local pin_entry = self.pin_entries[control]
        local pin       = pin_entry[1]:read()
        local mode      = pin_entry[2]:read()
        local remark    = pin_entry[3]:read()
        local entry     = mode .. pin .. trim(remark)
        local x1,x2     = string.find(pin_defs,',.'..pin..'.-,')
        if x1 ~= nil then   -- Eintrag ist vorhanden, muss jetzt geandert werden
            local old_text = string.sub(pin_defs,x1,x2)
            pin_defs       = string.gsub(pin_defs,old_text,','..entry..',')
        else   -- Eintrag ist noch nicht vorhanden, muss erzeugt werden
            pin_defs = pin_defs .. entry .. ','
        end
        self.pin_definitions:write(pin_defs..',')
        self:update_list()
        if mode == 'A' then
            local nr_of_pin = 0
            while nr_of_pin < 10000 and tostring(self.pin_list[nr_of_pin]) ~= pin do
                nr_of_pin = nr_of_pin + 1
            end
            self.pin_offset = 10 * math.floor(nr_of_pin/10)
--            print(self.pin_offset)
        end
--        self.pin_offset = 0
        self:pin_screen()
    end

end

--**********************************************************************

function App.read_pin_defs (self)

    local pin_defs = self.pin_definitions:read()
    local x1,x2    = string.find(pin_defs,'.-,,')
    if x1 == nil then
        pin_defs = ','
    else
        pin_defs = string.sub(pin_defs,x1,x2-1)
    end
    return(pin_defs)
    
end

--**********************************************************************

function App.check_if_tarif2_is_necessary (self)

    local rpd = self:read_pin_defs()
    if string.find(rpd,',E') then
        self.is_tarif2:set_visiable(0)
    end

end

--**********************************************************************

function App.pin_screen (self)  --  schreibt einen PIN-Screen

    local pin_defs = self:read_pin_defs()
    
 --   print('PIN_DEFS',pin_defs)

--    self:get_pin(self.pin_offset + self.ANZAHL_PINS + 1)
    local zaehler = 0
    while zaehler < self.ANZAHL_PINS do
        zaehler      = zaehler + 1
        local entry  = self.pin_list[zaehler+self.pin_offset]
        local mode   = ' '
        local remark = ''
        local x1,x2  = string.find(pin_defs,',.'..tostring(entry)..'.-,')
        if x1 ~= nil then
            mode   = string.sub(pin_defs,x1+1,x1+1) 
            remark = string.sub(pin_defs,x1+7,x2-1)
        end 
--        print('WWW',entry,mode,remark,zaehler)
        self.pin_entries[zaehler][1]:write( entry   )
        self.pin_entries[zaehler][2]:write( mode    )
        self.pin_entries[zaehler][3]:write( remark  )
    end
    
    self.pin_admin:activate()

end

--**********************************************************************

function App.update_list (self)

    math.randomseed(tonumber(self.pin_randomseed:read()))
    
    local new_list = {}
    local zaehler  = 0

    local pin_defs = self:read_pin_defs()

    while 0 == 0 do
        nr = math.random(10009,99991)
        if #self.pin_list > 0 then
            table.remove(self.pin_list)
        end
        local x1,x2  = string.find(pin_defs,',.'..tostring(nr)..'.-,')
        local sortnr = '9'
        if x1 ~= nil then
            zaehler = zaehler + 1
            if string.sub(pin_defs,x1+1,x1+1) == 'A' then
                sortnr = '1'
            end
            pin_defs = string.gsub(pin_defs,',.'..tostring(nr)..'.-,',',')
        end
        table.insert(new_list,{sortnr..tostring(nr),nr})
        if #pin_defs < 4 and #self.pin_list == 0 then
            if #new_list % self.ANZAHL_PINS == 0 and #new_list > 1.3*zaehler then
                break
            end
        end
    end
        
    table.sort(new_list,function (a,b) return(a[1]<b[1]) end)
    for i,nr in ipairs(new_list) do
        table.insert( self.pin_list, tonumber(nr[2]) )
    end

end

--**********************************************************************

function App.pin_list_change (self)   --  Aendert alle PINs mittels einer neuen Random-Seed

    math.randomseed(tonumber(self:time())*1000+tonumber(get_timer_value(29)))
    self.pin_randomseed:write( tostring(math.random(1030507,9030507)) )
    print("----------------------")    
--    print("RS1",self.pin_start:read())
    local rs = fcheck(0,tonumber,self.pin_start:read())
--    print("RS2",rs)
    if rs == nil then
        rs = 0
    end
    if 1000 <= rs and rs <= 9999999 then
        self.pin_randomseed:write( tostring(rs) )
    end
    
    
    math.randomseed(tonumber(self.pin_randomseed:read()))

    local pin_defs = string.sub( self:read_pin_defs(), 2 )
    local pins_new = ','
    while #pin_defs > 2 do
        local x1,x2 = string.find(pin_defs,'.-,')
        pins_new    = pins_new .. string.sub(pin_defs,1,1) .. math.random(10009,99991) .. string.sub(pin_defs,7,x2-1) .. ','
--        print(pins_new)
        pin_defs    = string.sub(pin_defs,x2+1)
    end
    
    self.pin_definitions:write(pins_new..',')
    local pin_defs1 = self:read_pin_defs()
--    print('PP',pin_defs1)
    self.pin_list = {}
    
end


--**********************************************************************

function App.activate_ticketslot (self)

    local tan = self.ticketslot_tan1:read() .. self.ticketslot_tan2:read() .. self.ticketslot_tan3:read() 

--    set_text(14,11,tostring(self.flash_is_not_signed) .. ' ' .. tostring(self.report_is_integer))

    self.no_timeout = 0
    local rid       = self.request_id
    self.request_id = 0
    self:renew_timeout()
    
--    print('SR',rid)

    if self.flash_is_not_signed == 1 and rid == 0 then   --  falls jemand einfach beim Speicher signieren
        self:disable_device()         --  auf OK drueckt, ohne vorher eine TAN angefordert zu haben.
        return                        --  Denn nur der Fall, dass die Daten auf der SD-Karte falsch signiert
    end                               --  sind, soll toleriert werden   
    

    if rid > 86000 then

        if self:check_tan(0, tostring(rid) ,tan) == 1 then
            if rid > 93000 then
                self:check_flash_signature('write new signature')
                self.flash_is_not_signed = 0
            end
            self.normal_test1 = "1436"
            local reports = self:read_report()
            self.report_is_integer = 1
error2("wr80")
            self:write_report(reports)
error2("wr81")
            self:update_counter(0)
            set_backlight(self.BLACKSCREEN)
            self.standby:activate()
        end
        
        if self.flash_is_not_signed == 1 then
            print('not ready')
            self:disable_device ()
            return
        end
        
--        print('FR',self.flash_is_not_signed,self.report_is_integer)
        
        if self.report_is_integer == 1 then
            return
        end

    end
    
    if self.report_is_integer == 0 then
        self.report_is_integer = 2  -- 2 weil wir mit nicht signiertem reportfile arbeiten
        self:update_counter(0)
        set_backlight(self.BLACKSCREEN)
        self.standby:activate()
        return
    end
    
    local stored_pins = self.ticketslot_pin:read()
    local pin1        = ''
    self.no_timeout   = 1
    self.admin:activate()
    while #stored_pins > 4 do
        if file_open ~= nil then
            feed_dog()
        end
        self:renew_timeout()
        local pin   = string.sub(stored_pins,1,5)
--        print(pin)
        stored_pins = string.sub(stored_pins,6)
        if self:check_tan(0, pin, tan) == 1 then
            local ri = self.report_is_integer
            self.normal_test1 = "1437"
            local reports = self:read_report()
            self.ticketslot_pin:write(pin1 .. stored_pins)
            self:update_counter(300000)
            self.report_is_integer = ri            
error2("wr83")
            self:write_report(reports,self:date("%y%m%d%H%M%S").."X","0,yy")
error2("wr84")
            return
        else
            pin1 = pin1 .. pin
        end
    end
    
end

--**********************************************************************

function App.disable_device (self,no_screen_change)

    for i = 0, 31, 1 do
        stop_timer(i)
    end
    
    if no_screen_change == nil then
        set_backlight(100)
        lang_nr = self.lang_nr_for_report
        self.not_ready:activate("force")
    end
    self.no_timeout      = 1
    self.uart_sperre     = 1
    self.last_knob_action_was_press  = 0
    DISABLED             = 1

end

--**********************************************************************


function App.heartbeat_entry (self)

    if self.ticket_wird_gerade_gedruckt == 0 then
        local new_print_date = self:date("%y%m%d%H%M%S")
        if string.sub(new_print_date,1,10) ~= string.sub(self.last_print_date,1,10) then   --  man braucht nur jede Stunde einen Eintrag
            self.normal_test1 = "1438"
            local reports = self:read_report()
            if self.ticket_wird_gerade_gedruckt == 0 then
error2("wr85")
                self:write_report(reports,new_print_date..".","0,00")
error2("wr86")
                self.last_print_date = new_print_date
            end
        end
    end

end


--**********************************************************************


function App.on_timer (self,tid)

--    print('Timer call'..tostring(tid))

    if tid == 0 then
--        self.uart_sperre = 0
        self.normal_test1 = "1439"
        local reports = self:read_report()  -- die Daten in das Ticketreporting reinschreiben
        local betr    = self.anzeige_betrag:read()
        if trim(betr) ~= '0,00' and self.real_coins == 1 then
error2("wr87")
            self:write_report(reports,self:date("%y%m%d%H%M%S")..CURRENTSHORT,betr)
error2("wr90")
--            self:write_report(reports,self:date("%y%m%d%H%M%S").."A",betr)
            self:reset_coin()
--            self.anzeige_betrag:write('0,00')
        end
        self.no_timeout = 0
        set_backlight(self.BLACKSCREEN)
        self.standby:activate()

    
    elseif tid == 1 and self.no_timeout == 0 then  --  @@
--        self.uart_sperre = 0
        self.pin_feld:write('')
--        self.sondertarif_anzeige:set_visiable(1)
--        self.tarifnr = 1
--        self.anzeige_betrag:write('0,00') 
        self.ungueltig = 0
        self:switch_language(1)
        set_backlight(self.BLACKSCREEN)
        self.standby:activate()

    elseif tid == 2 then
        self:reset_coin()
        self.admin_count = 0
--        self.uart_sperre = 0
        if self:read_counter() == 0 or self.there_is_no_sdcard == 1 then
            print("READ COUNTER is 0")
            self:disable_device()
            return
        else
            self.no_timeout = 0
            self:renew_timeout()
            self:switch_language(1)
            self:choose_flags()
            self.lang:activate()
        end
    
    elseif tid == 3 and self.no_timeout == 0 then 
--        self.uart_sperre = 0
        if file_open == nil then
            print('LOW BACKLIGHT')
        end
        set_backlight(self.LOW_BACKLIGHT)
        self.ueberdeckung:set_visiable(1)
        if file_open == nil then
            self.show_low:set_visiable(1)
        end

    elseif tid == 4 then
        self.admin_count = 0
        self.control0    = nil

    elseif tid == 5 then
--        self.uart_sperre = 0
        local text = trim(self.pin_feld:read())
        self.pin_feld:write( string.sub(text,1,#text-1) )
        self:renew_timeout()
        if #text == 1 then
            self.pin_wartezeit = 0
            self.ungueltige_pin:set_visiable(1)
        else
            start_timer(5,self.pin_wartezeit,0,1)
        end

    elseif tid == 6 then
--        uart_send_data( self:str_to_byteArray('     '))
--        uart_send_data( self:str_to_byteArray('     '))
--        uart_send_data( self:str_to_byteArray('     '))

--    elseif tid == 7 then
--        self:uarthandler()

    elseif tid == 7 then
        self:print_parkticket()
--        self.uart_sperre = 0

    elseif tid == 8 then
        self:on_control_notify1(ACT_SCREEN,ACT_TASTE)    --   fuer Tests
        self:test1_simu_taste()

    elseif tid == 9 then
--        table.insert(self.uartbuffer,0)
        self.long_press    = 1
--        self.catch_release = 1
        self:on_uart_recv_data( { [0] = 0xEE, [1] = 0xB1, [2] = 22 }  )
--        self:on_control_notify1( self:current_screen().screen_id , 80)

    elseif tid == 14 then
        feed_the_dog()
        start_timer(14,4000,0,1)

    elseif tid == 15 then
        self.zeit_umstellen()

    elseif tid == 16 then
        self.uart_sperre = 0
        self:print_testausdruck()

    elseif tid == 17 then
        self:reset_coin()
        self:renew_timeout()

    elseif tid == 18 then        --  ruecksetzen nach mehrfacher (7) falscher QR-Code Eingabe
        self.no_timeout = 0
        print("QRR",self.qrzaehler)
        if self.qrzaehler > 5 then
            self:on_timer(2)
        else
            self.coin:activate()
        end
        self:renew_timeout()
       if not (self.start_test_nr == "") then
           self:start_test("")
       end
        
    elseif tid == 19 then
        self.qr_invalid:set_visiable(-1)
        self.qrtan:write("")

    elseif tid == 20 then                     --  Testprogramme, laufen nach 5 Sekunden, dazu muss im QR-Eingabe Modus (virtuelle Muenzen)
        self.nochmal:set_visiable(-1)
        self.nochmal_button:set_visiable(-1)
        self.lang:activate()
        self:start_test(self.start_test_nr)   --  eine fuenfstellige Nummer, die mit 0 beginnt, eingegeben werden
    
    elseif tid == 21 then
        self:on_uart_recv_data( { [0] = 0xEE, [1] = 0xB1, [2] = 21 } )

    elseif tid == 22 then
        self:on_uart_recv_data( { [0] = 0xEE, [1] = 0xB1, [2] = 22 } )

    elseif tid == 23 then
        
        feed_the_dog()
        local drucke_zeilen_jetzt = 1
        local zeilen_jetzt        = {}
        local zeilen_spaeter      = {}
        local pausen_interval     = 10

        for i,zeile in ipairs( self.print_data ) do
            if drucke_zeilen_jetzt == 0 then 
                table.insert(zeilen_spaeter,zeile)
            elseif string.sub(zeile,1,5) == "PAUSE" then
                drucke_zeilen_jetzt = 0
                feed_the_dog()
                pausen_interval     = tonumber(string.sub(zeile,6,99))
            else
                table.insert(zeilen_jetzt,zeile)
            end
        end
        self.print_data = zeilen_spaeter
--        print("ANZZ",#self.print_data)

        feed_the_dog()
        self:send_to_printer1( zeilen_jetzt )
        feed_the_dog()

        if #self.print_data == 0 then
            start_timer(24,50,0,1)   --  keine Daten mehr da, Druck beenden
        else
            start_timer(23,pausen_interval,0,1)  --  um fuer den Schnitt des Papiers Zeit zu lassen
        end
        
    elseif tid == 24 then
        print("VISIABLE OFF")
        self.report_wird_gedruckt:set_visiable(0)
        if self.delete_printbuffer == 1 then
error2("newbuf01")
                my_write_filedata1('printbuffer.txt',sd_dir..'/printbuffer.txt',self:crypt( sec_key3,'                     ',80),over_write)
error2("newbuf02")
             --   der lange Leerzeichen-String ist notwendig, weil sonst die Verschluesselung nicht funktioniert!
        end

    elseif tid == 25 then
        self.debug1:write("Start to generate report with thread")
        self.report_data = {}
        function thr1 () self:generate_report() end
        xzaehl = 0
        if file_open == nil then
            WAIT_26 = 10
        end
        self.generate_report_thread = coroutine.create( thr1 )
        coroutine.resume(self.generate_report_thread)
        
    elseif tid == 26 then
--        self:renew_timeout()   @@  aktivieren fuer test
        xzaehl = xzaehl + 1
        self.debug1:write("Resume ROUND "..tostring(xzaehl))
        coroutine.resume(self.generate_report_thread)

    elseif tid == 27 then  --  Timer-Pufferung des ggfs. recht langen Intervalls bis zur naechsten Zeitumstellung
        self.zeitumstellungs_zaehler = self.zeitumstellungs_zaehler + 1
        if self.zeitumstellungs_zaehler > 200 then
            self:zeit_umstellen()
        else
            start_timer(27,self.zeitumstellungs_intervall,0,1)
        end

    elseif tid == 28 then
        self:insert_coin(self.nr_coin)

    elseif tid == 29 then  --  heartbeat mit Eintraegen in die newreport.txt
        self:heartbeat_entry()
        print("heartbeat")
        start_timer(29,3300000,0,1)  --  alle 55 Minuten einen Heartbeat versuchen
    end

end

--**********************************************************************

function App.xxon_uart_recv_data(self,packet,real_knob)

--    print("UART_SPERRE",uart_sperre)
--    if self.uart_sperre == 1 then
--        return
--    end

    if HAS_KNOB ~= 1 and real_knob ~= nil then
        local bed  = HAS_KNOB == 0
        HAS_KNOB   = 1
        self:coins_and_knob(bed)
    end

    if packet[0]==0xEE and packet[1]==0xB1 then
        if packet[2]==0x10  then
            if     packet[3] ==  0x01 then coin_nr = 1081
            elseif packet[3] ==  0x02 then coin_nr = 1082
            elseif packet[3] ==  0x05 then coin_nr = 1083
            elseif packet[3] ==  0x0a then coin_nr = 1084
            elseif packet[3] ==  0x14 then coin_nr = 1085
            end
            self:on_control_notify1( self:current_screen() ,coin_nr)
        elseif packet[2] == 21  then
            self:press_action()
        elseif packet[2] == 22  then
            self:release_action()
        end
    end

end

--**********************************************************************

function App.on_uart_recv_data(self,packet,real_knob)

    if self.uart_sperre == 1 then
        return
    end


    if HAS_KNOB == 0 and real_knob ~= nil then  --  oh, es ist ja DOCH ein Knopf da!
        HAS_KNOB = 1
        self.knob1:set_visiable(-1)
    end

    local screen = self:current_screen()
    if screen == nil then
        screen = self.standby
    end
--    print("GCS",get_current_screen())

--    local pr_re = 1
--    print("SID",screen,self.standby)
--    if screen == self.standby then
--        pr_re = 2
--    end
    local pr_re = self.press_release

--    print("PR_RE",pr_re,screen)

    if packet[0]==0xEE and packet[1]==0xB1 then
        if packet[2]==0x10  then
            if     packet[3] ==  0x01 then coin_nr = 1081
            elseif packet[3] ==  0x02 then coin_nr = 1082
            elseif packet[3] ==  0x05 then coin_nr = 1083
            elseif packet[3] ==  0x0a then coin_nr = 1084
            elseif packet[3] ==  0x14 then coin_nr = 1085
            elseif packet[3] ==  0x18 then coin_nr = 1086
            elseif packet[3] ==  0x22 then coin_nr = 1089
            end
            self:on_control_notify1( self:current_screen() ,coin_nr)
        elseif packet[2] == 21  then   --  press knob
--    print("PP",21,pr_re)
--            table.insert(self.uartbuffer,1)
--            if self.uart_sperre == 1 then
--                return
--            end
            self.last_knob_action_was_press   = 1
            self.long_press       = 0
--            self.long_was_pressed = 0
            if pr_re > 0 then
                stop_timer(9)
                self.akt_screen = screen
                start_timer(9,1500,0,1)
            end
--            set_text(1,21,"PRESS")
--            set_text(1,22,"--")
--            set_backlight(30)
--            print(screen)
            if pr_re < 2 then
                self:on_control_notify1( screen.screen_id , 80)
            end
        elseif packet[2] == 22  then  -- release knob
            if pr_re > 0 then
                stop_timer(9)
            end
--            set_text(1,22,"RELEASE")
--            set_text(1,21,"--")
--            print("LWP",self.last_knob_action_was_press)
            if self.last_knob_action_was_press == 1 and (self.long_press == 1 or pr_re == 2) then
                self:on_control_notify1( screen.screen_id , 80)
--            else
--                if self.long_was_pressed == 1 then
--                    self:on_control_notify1( self:current_screen().screen_sid , 80)
--                end
            end            
            self.last_knob_action_was_press = 0
        end
    end

end



--**********************************************************************

function App.press_action (self)

--  print('              PP',21)
--      table.insert(self.uartbuffer,1)
    local pr_re, screen = self:get_pr_re()
    self.last_knob_action_was_press   = 1
    self.long_press       = 0
    self.long_was_pressed = 0
    if pr_re > 0 then
        stop_timer(9)
        self.akt_screen = screen
        start_timer(9,1500,0,1)
    end
    set_text(1,21,'PRESS')
    set_text(1,22,'--')
    set_backlight(self.LOW_BACKLIGHT)
--    print(screen)
    if pr_re < 2 then
        self:on_control_notify1( screen.screen_id , 80)
    end

end


--**********************************************************************

function App.release_action (self)

--    print('            PP',22)
--    table.insert(self.uartbuffer,0)
    local pr_re, screen = self:get_pr_re()
    if pr_re > 0 then
        stop_timer(9)
    end
    set_text(1,22,'RELEASE')
    set_text(1,21,'--')
--    print('LWP',self.last_knob_action_was_press)
    if self.last_knob_action_was_press == 1 and (self.long_press == 1 or pr_re == 2) then
        self:on_control_notify1( screen.screen_id , 80)
--    else
--        if self.long_was_pressed == 1 then
--            self:on_control_notify1( self:current_screen().screen_sid , 80)
--        end
    end            
    self.last_knob_action_was_press = 0

end

--**********************************************************************

function App.get_pr_re (self)

    local screen = self:current_screen()
    if screen == nil then
        screen = self.standby
    end
--    print('GCS',get_current_screen())

    local pr_re = 1
--    print('SID',screen,self.standby)
    if screen == self.standby then
        pr_re = 2
    end
--    print('PR_RE',pr_re)

    return pr_re, screen
    
end


--**********************************************************************

function App.on_uart_recv_data1(self,packet)    --   UART Test

    if packet[0]==0xEE and packet[1]==0xB1 then
        if packet[2]==0x10  then
            if     packet[3] ==  0x01 then coin_nr = 1081
            elseif packet[3] ==  0x02 then coin_nr = 1082
            elseif packet[3] ==  0x05 then coin_nr = 1083
            elseif packet[3] ==  0x0b then coin_nr = 1084
            elseif packet[3] ==  0x14 then coin_nr = 1085
            end
        elseif packet[2] == 21  then
            set_text(1,21,'PRESS')
            set_text(1,22,'--')
        elseif packet[2] == 22  then
            set_text(1,22,'RELEASE')
            set_text(1,21,'--')
        end
    end

end


--**********************************************************************

function App.uarthandler (self)   --  fruehere Version, indirekte Aufrufe

    local pr_re = self.press_release

    pr_re = 1

    local knob_event = 0
    while 0 == 0 do
    
        self.uartzaehler = self.uartzaehler + 1
        knob_event = self.uartbuffer[ self.uartzaehler ]
        if knob_event == nil then
            self.uartzaehler = self.uartzaehler - 1
            break
        end
    
        if knob_event == 1 then
            self.long_press = 0
            self.last_knob_action_was_press = 1
            if pr_re < 2 then
                self:on_control_notify1( self:current_screen().screen_sid , 80)
            end
        else
            if self.last_knob_action_was_press == 1 then
                self.last_knob_action_was_press = 0
                if self.long_press == 1 and pr_re == 1 or pr_re == 2 then
                    self:on_control_notify1( self:current_screen().screen_sid , 80)
                end
            else
                if self.long_press == 0 then
                    self:on_control_notify1( self:current_screen().screen_sid , 80)
                end
            end
        end
    end

    start_timer(7,30,0,1)
    
end


--**********************************************************************

function App.xxuarthandler (self)

    local knob_event = 0
    while 0 == 0 do
        if #self.uartbuffer == 0 then
            break
        end
    
        knob_event = table.remove(self.uartbuffer,1)
        if knob_event == 1 then
            self.long_press = 0
            self.last_knob_action_was_press = 1
            self:on_control_notify1( self:current_screen().screen_sid , 80)
        elseif knob_event == 0 or knob_event == 9 then
            if knob_event == 0 and self.last_knob_action_was_press == 1 and self.long_press == 1 then
                self:on_control_notify1( self:current_screen().screen_sid , 80)
            end
            self.last_knob_action_was_press = 0
        end
    end

    start_timer(7,30,0,1)
    
end


--**********************************************************************

function App.xxcheck_for_event (self,scr)

--    print('check for event')

    if self.last_press_button ~= self.press_button:read() then
--        if self.ticket_wird_gerade_gedruckt == 0 then
            self:on_control_notify1(scr,80)
--        end
        self.press_button:write('released')
        self.last_press_button = 'released'
    end
    
    if self.last_pluse_sum ~= self.pluse_sum:read() then
        local coin_diff     = tonumber(self.pluse_sum:read()) - tonumber(self.last_pluse_sum)
        self.last_pluse_sum = self.pluse_sum:read()
        if coin_diff > 0 then
            if     coin_diff ==  1 then self:on_control_notify(scr,1081)
            elseif coin_diff ==  2 then self:on_control_notify(scr,1082)
            elseif coin_diff ==  5 then self:on_control_notify(scr,1083)
            elseif coin_diff == 10 then self:on_control_notify(scr,1084)
            elseif coin_diff == 20 then self:on_control_notify(scr,1085)
            else print('coin evaluation error') end
        end
    end
    
end

--**********************************************************************

function App.renew_timeout(self)

    set_backlight(100)
    self.ueberdeckung:set_visiable(-1)
    self.show_low:set_visiable(-1)
--    self.nochmal:set_visiable(-1)
    stop_timer(0)
    stop_timer(1)
    stop_timer(3)
    stop_timer(18)
    if self.no_timeout == 0 then
        start_timer(0,self.TIMEOUTLONG*1000,0,1)
        start_timer(1,self.TIMEOUT*1000,0,1)
        start_timer(3,self.REDUCE_BACKLIGHT*1000,0,1)
    end

end


--**********************************************************************

function App.renew_qr_timeout(self)

    set_backlight(100)
    self.ueberdeckung:set_visiable(-1)
    self.show_low:set_visiable(-1)
--    self.nochmal:set_visiable(-1)
    stop_timer(0)
    stop_timer(1)
    stop_timer(3)
    stop_timer(18)
    start_timer(18,self.TIMEOUTQRCODE*1000,0,1)
    start_timer(3,self.QRCODE_REDUCE*1000,0,1)

end


--**********************************************************************

function App.start_test (self,nr)


    if SERIALNUMBER ~= "9999999" then
        return
    end

    if nr == "" then
        self:on_timer(20)
        return
    end
    
    print("Test No.",nr)

    if nr == "03301" then
        self:test1_generate_report("xx")
    end

    if nr == "03302" or nr == "03303" then
        self:test1_print_big_qrcode("xx",nr)
    end

    if  nr == "03305" or nr == "03306"  then
        
        self.simu_par    = nr
        self.tastenfolge = {101}
        self:test1_simu_taste()
        
    end

end

--**********************************************************************
--private

Screen         = {}
Screen.__index = Screen

--**********************************************************************

function Screen:new (app,screen_id,multilang)

    local self     = setmetatable({},Screen)
    self.app       = app
    self.screen_id = screen_id
    self.act_lang  = -1  --  language is undefined
    if multilang == nil then
        self.multilang_fields = {}
    else
        self.multilang_fields = multilang
    end
    self.app.screens[tonumber(screen_id)] = self
    self.pr_re = self.app.press_release
    return self
    
end

--**********************************************************************

function Screen.get_text (self,nr)

    if self.app.simulation == 1 then
        text = self.app.field_sim[tostring(self.screen_id)..'.'..tostring(nr)]
        if text == nil then
            text = ''
        end
    elseif nr > 0 then
        text = get_text(self.screen_id,nr)
--    else
--        local erg = get_value(self.screen_id,-nr)
--        erg       = tonumber(erg)
--        if erg < 0.5 then
--            text = '0'
--        else 
--            text = '1'
--        end
    end
    return text

end

--**********************************************************************

function Screen.set_text (self,nr,text)

    if self.app.simulation == 1 then
        self.app.field_sim[tostring(self.screen_id)..'.'..tostring(nr)] = text
    elseif nr > 0 then
        local ggg = self.screen_id
--        ZF1 = tostring(math.random(1001,99942))
        set_text(ggg,nr,text)
--        ZUFALLSWERT = ZF1 .. " " .. tostring(math.random(1001,99942)) .. " " .. tostring(self.screen_id) .. " " .. tostring(nr)
--                                .. " " .. text
--    else
--        set_value(self.screen_id,-nr,text)
    end
    
end

--**********************************************************************

function Screen.activate (self,force)

--    self.screen_id = self.app.no_sdcard.screen_id
--    return("")

    self.screen_id = self.app.no_sdcard.screen_id  set_backlight(100)   --  SWITCH main0.lua

    if self.act_lang ~= lang_nr then
        force = "force"
    end
    
    if force == nil then
        if self.app:current_screen() == self then
            return
        end
    else
        self.act_lang = -1
    end
    
    if self.act_lang ~= lang_nr then
        for i,nr in ipairs(self.multilang_fields) do
            local text = self:get_text(nr)
            if text == "-" or text == "" or string.find(text,"+") or string.find(text,"%%") 
                           or string.find(text,"?") or string.find(text,"'") then
                text = "S" .. tostring(self.screen_id) .. "F" .. tostring(nr)
            end
--            print(nr,text,t(text))
            
            self:set_text(nr,t(text))
        end
    end

    if self.app.simulation == 0 then
        if self.app.screen_switch_is_blocked == 0 then
            change_screen(self.screen_id)
            ACT_SCREEN = self.screen_id
        end
    end
    
    self.act_lang          = lang_nr
    self.app.press_release = self.pr_re

end


--**********************************************************************
--private

Field         = {}
Field.__index = Field

--**********************************************************************

function Field:new (app,default_text,screen,control_nr)

    local self = setmetatable({},Field)
    self.app                 = app
    if default_text == nil then
        default_text = ''
    end
    self.default_text        = default_text
    self.screens             = {}
    self.flash_position      = nil
    if screen ~= nil then
        self:add_field(screen,control_nr)
    end
    
    if tonumber(self.app.use_default_values) == 1 then
        self:write(default_text,'no signature')
    end
    
    return self
    
end

--**********************************************************************

function Field.add_field (self,screen,control_nr)

    table.insert(self.screens,{screen,control_nr})
--    print(screen,control_nr,self.default_text)
    if self.app.simulation == 1 then
        screen:set_text(control_nr,self.default_text)
    end

end    

--**********************************************************************

function Field.add_flash_position (self,offset,laenge)

    self.flash_position = offset
    self.flash_length   = laenge
    
    table.insert(self.app.fields,self)
    if self.simulation == 0 then
        self:read()  -- for immediate synchronizing screen fields and flash
    end

end    

--**********************************************************************

function Field.read (self)

    if self.zwischenspeicher ~= nil then
        return(self.zwischenspeicher)
    end

    local erg = nil
    for i,control in ipairs(self.screens) do
        local screen     = control[1]
        local control_nr = control[2]
        local erg1       = screen:get_text(control_nr)
        if erg == nil then
            erg = erg1
        elseif erg ~= erg1 then
            erg = nil  -- die Screens stimmen nicht ueberein
            break
        end
    end
--    print('EE',erg)
    
    local flash_store = nil  -- Flash Store auch auslesen
    if self.flash_position ~= nil then
        if self.app.simulation == 0 then
            flash_store = read_flash_string(self.flash_position)
        end
        if flash_store == nil then
--            print("READ",self.flash_position,flash_store)
            if self.app.simulation == 0 then
                write_flash_string(self.flash_position,self.default_text)
            end
            flash_store = self.default_text
        end
        if erg ~= flash_store then
            self:update_screens(flash_store)
            erg = flash_store
        end
    else
        if erg == nil then
            self:update_screens(self.default_text)
            erg = self.default_text
        end
    end
    return(erg)

end

--**********************************************************************

function Field.write (self,text)

    self:update_screens(text)

    if self.flash_position ~= nil then
        self.zwischenspeicher = text
        self.app:check_flash_signature('update')
        self.zwischenspeicher = nil
        write_flash_string(self.flash_position,text)
    end
        
end

--**********************************************************************
            
function Field.update_screens (self,flash_store)

    for i,control in ipairs(self.screens) do
        local screen     = control[1]
        local control_nr = control[2]
        if control_nr == nil then
            error1("update_screens")
        else
            screen:set_text(control_nr,flash_store)
        end
    end

end

--**********************************************************************

function Field.set_visiable (self,is_visiable)

    for i,sf in ipairs(self.screens) do
--        print('XX',sf[1].screen_id,sf[2],is_visiable)
        if self.app.simulation == 0 then
            set_visiable(sf[1].screen_id, sf[2], math.max(0,is_visiable) )
            if is_visiable < 0 then
                set_enable(sf[1].screen_id, sf[2], 0 )
            else
                set_enable(sf[1].screen_id, sf[2], 1 )
            end
        end
    end
    
end 

--**********************************************************************

function Field.synchronize_with_entry (self,screen,control)

    for i,scr in ipairs(self.screens) do
--        print(screen.screen_id,control,scr[1].screen_id,scr[2])
        if screen == scr[1] and control == scr[2] then
            local val = screen:get_text(scr[2])
--            print(screen,control,val)
--            print('QQ',control,val)
            self:write(val)
            screen:activate()
            return 1
        end
    end
    
    return 0

end
        
--**********************************************************************

function xxcheck_initialization_string (nr,init_string)

--  Wenn nr1 == '', dann wird eine Zufallsnummer ausgegeben
--  Wenn init_string == '', dann wird der zu nr zugehoerige init:string ausgegeben
--  Ansonsten wird eine Pruefung gegen den init_string gemacht (Ergebnis 0 oder 1)

    local nr1 = 0

    if nr == '' then
        math.randomseed(os.time())
        nr1 = tostring(math.random(11111,99999))
    else
        nr1 = tostring(nr)
    end
    
    if nr == '' then
        return nr1
    end

    local p_a = tonumber(string.sub(nr1,1,2)) + 2
    local p_b = tonumber(string.sub(nr1,3,4)) + 5
    local p_c = tonumber(string.sub(nr1,5))   + 1
    
    local sk_bytes = bin.hextobin(string.sub(sec_key,1,64) .. rk)
    local text     = {}  -- {[1] = '0'}
    while #text < 24 do
        local x = #text
        local y = (p_a + p_b * x + p_c * x * x) % #sk_bytes + 1
        table.insert(text,sk_bytes[y])
    end
    
    text = bin.bintohex(text)
    text = bin.hextos(text)
    text = base27_encode(text)
    text = string.sub(text..'XX',1,5)
    
    if init_string == '' then
        return(text)
    elseif init_string == text then
        return(1)
    else
        return(0)
    end
    
end

--**********************************************************************

function xxupdate_screen_data_from_flash ()

    local text_gesamt = ''
    for k,v in pairs(flash_data) do
        local text1 = trim(read_flash_string(v[1],v[2])) 
        set_text(v[3],v[4], text1 )
        text_gesamt = text_gesamt .. text1
    end

end

--**********************************************************************

function xxsign_flash ()

    local text = md5_flash()
--    print('MD5_new',text)
    local ciph = crypto_crypt(sec_key,text)
--    print ('CIPH_new',ciph)
    local text1 = crypto_decrypt(pub_key,ciph)
--    print('VVV',text1)
    write_flash_string(posmax+10,64, ciph )

end

--**********************************************************************

function xxmd5_flash ()

    local contents = {}

    for i,v in pairs(flash_data) do
--        print(v[1])
--        print(read_flash_string(v[1],v[2]))
        table.insert(contents,{ v[1], trim(read_flash_string(v[1],v[2])) } )
    end
    
    table.sort(contents,function (a,b) return(a[1]<b[1]) end)

    local text = 'x'
    for i,text1 in ipairs(contents) do
        text = text .. text1[2]
    end
    
    text = bin.stohex( md5lib.hash(text) )
    return text
    
end    

--**********************************************************************

function xxread_counter ()

    local zaehler = 0
    local adr     = 50000
    counter       = 0
    local c_min   = 999999
    local nrmin   = 0
    nrmax         = 0
    posmax        = 0
    posmin        = 0

    while zaehler < 10 do
        pos      = adr + zaehler*4096
        counter1 = tonumber(read_flash_string(pos,10))
        if counter1 ~= nil and counter1 > counter then
            counter = counter1
            nrmax   = zaehler
            posmax  = pos
        end
        if counter1 ~= nil and counter1 < c_min then
            c_min  = counter1
            nrmin  = zaehler
            posmin = pos
        end 
        zaehler = zaehler + 1
    end

end


--**********************************************************************

function xxcrypto_make_pin (setrandom)

--  erstmal eine Zufallszahl generieren, die dann als zu uebertragender Plaintext gilt
    local plainnumber_bin = {}
    local plainnumber     = 0

    if setrandom == 1 then
        local f  = file_open('/proc/self/stat','r')
        math.randomseed(os.time()+os.clock()+tonumber(string.sub(f:read(),1,4)))
        f.close()
    end
    while #plainnumber_bin < 29 do
        local x = math.random(0,1)
        table.insert(plainnumber_bin,tostring(x))
        plainnumber = 2*plainnumber + x  --  das wird die plainnumber in dezimaler Form
    end

--    print('P1',table.concat(plainnumber_bin))

--  die plainnumber jetzt verschluesseln
    plainnumber        = tostring(math.floor(plainnumber + 110000000.1))
--    print('P1',plainnumber)
    local ciphernumber = crypto_crypt(sec_key,plainnumber)
--    print('P3',bin.stohex(ciphernumber))

--    print(plainnumber)
--    print(bin.stohex(ciphernumber))
    
--  aus der ciphernumber jedes 9te Bits herausholen
    local cipher_bits  = crypto_pick_bits(ciphernumber)
--    print('CC',table.concat(cipher_bits))

--  jetzt plainnumber im binaeren format und die stichprobenartig gewaehlten Bits mischen:
    local text    = {}
    local zaehler = 0
    while zaehler < 29 do
        zaehler = zaehler + 1
        table.insert(text,plainnumber_bin[zaehler])
        local x = cipher_bits[zaehler] 
--        if x ~= nil then
            table.insert(text,x)
--        end
    end  
--    print(table.concat(text))

    
--  Wir haben jetzt 48 Bit in text. Diese machen wir jetzt zu einem String mit 6 Byte,
--  den wir erst noch mal RC4-verschluesseln:
    
    text = bin.bintohex(text)
    text = bin.hextos(text)
    rk1  = bin.hextos(rk)
    text = rc4.encrypt(rk1,text)
    
--    print('QQQ',bin.stohex(text))

--  Diese verschluesselten 6 Byte machen wir zu 10 Zeichen im base27 Alphabet:

    local pin = base27_encode(text)
    
--  Wegen 27^10 = ungefaehr 0.75 * 2^48 kommt es in 26,2 Prozent der Faelle vor, dass wir
--  11 Zeichen kriegen in pin. Das wollen wir aber nicht, daher nochmal rechnen fuer eine PIN
--- mit 10 Zeichen:

    if #pin > 10 then              --  Diese Rekursion kommt zu 99,5 Prozent nach 4 Runden zum Ende
        print('  repeat ...')
        return crypto_make_pin(0)  --  zu 99,9998 nach 10 Runden. Und braucht ausserdem wohl nur
    else                           --  im Desktop laufen, nicht im Geraet. Deswegen kein Problem.
        return pin
    end

end

--**********************************************************************

function xxcrypto_check_pin (pin)

--   Die PIN ist Base27 und RC4 verschluesselt, also erstmal entschluesseln,
--   dann zu einem Bitarray umwandeln

    pin = base27_decode(pin)
    rk1 = bin.hextos(rk)
    pin = rc4.decrypt(rk1,pin)
    pin = bin.stohex(pin)
    pin = bin.hextobin(pin)
    
    local plainnumber     = 0
--    local plainnumber_bin = ''
    local ciphertext_part = {}
    
    for i,v in ipairs(pin) do
        if i % 2 == 1 or i > 38 then  --  in diesen Bits ist die zu verschluesselnde Zahl
            plainnumber = 2*plainnumber + tonumber(v)
--            plainnumber_bin = plainnumber_bin .. v
        else    --  in diesen Bits ist eine Auswahl der verschluesselten Zahl
            table.insert(ciphertext_part,v)
        end
    end
    
--    print('P2',plainnumber_bin)
    
--  plainnumber ist die urspuengliche Plaintext Zahl
    local plainnumber1 = tostring(math.floor(plainnumber + 110000000.1))

--  diese wird jetzt mit dem secret key verschluesselt
    local ciphernumber = crypto_crypt(sec_key,plainnumber1)
--    print('P3',bin.stohex(ciphernumber))

--  alle Bits aus ciphertext_part sollten uebereinstimmen, pruefe das:
    if table.concat(crypto_pick_bits(ciphernumber)) == table.concat(ciphertext_part) then
        return plainnumber
    else
        return 0
    end

end

--**********************************************************************
    
function xxcrypto_pick_bits (ciphertext)

    ciphertext = bin.stohex(ciphertext)
    ciphertext = bin.hextobin(ciphertext)
    
    ciphertext_part = {}
    for i,v in ipairs(ciphertext) do
        if (i+2) % 9 == 0 and #ciphertext_part < 19 then
            table.insert(ciphertext_part,v)
        end
    end

    return ciphertext_part

end

--**********************************************************************
--  MAIN_SECTION_END
--**********************************************************************
--  CONSOLE_AREA_BEGIN
--**********************************************************************
--private

if arg ~= nil and arg[1] == 'patch'  then

    local parts = 25

    local patch1 = io.open(arg[2])
    local gesamt = 0
    while 0 == 0 do
        local line1 = patch1:read()
        gesamt = gesamt + 1
        if line1 == nil then
            patch1:close()
            break
        end
    end

    patch1        = io.open(arg[2])
    local zaehler = 0
    local total   = 0
    text          = {}

    local chunk = ''
    while 0 == 0 do

        local line1 = patch1:read()
        zaehler     = zaehler + 1

        if line1 ~= nil then
            chunk = chunk .. line1 .. "\n"
        end 
        if zaehler > math.max(10,gesamt/parts) or line1 == nil then
            total      = total + 1
            zaehler    = 0
--            print(chunk)
            if #chunk < 100 then
                chunk = chunk .. "\n\n--*************************************************************" ..
                                 "*****************************************************\n\n"
            end
            local ciph = trim(App.crypt('',pub_key1,chunk))
--            print("----->\n")  print(App.decrypt('',sec_key1,ciph))
            while 0 == 0 do
                table.insert(text,string.sub(ciph,1,80))
                ciph = string.sub(ciph,81)
                if #ciph == 0 then
                    break
                end
            end
            table.insert(text,'--')
            chunk = ''
        end

        if line1 == nil then
            patch1:close()
            break
        end

    end

    if arg[3] == nil then
        arg3 = ""
    else
        arg3 = arg[3] .. "_"
    end


    local fh = io.open('patch_'..arg3..SERIALNUMBER..'.enc','w')
    if fh ~= nil then
        fh:write("--"..tonumber(total).."\n")
        for i,zeile in ipairs(text) do
            fh:write(zeile.."\n")
        end
    end
    fh:close()
    
    local text1     = SERIALNUMBER .. '/301000/0'
    
    local text101   = [[
20240715060632.  0,00
20240715070134.  0,00
20240715074403Q  0,ph
20240715083541Q  0,te
20240715084115Q  0,te
20240715084942Q  0,em
20240715085049Q  0,ph
20240715091341E  1,00
20240715091942E  1,00
20240715092710Q  0,bo
20240715094148E  2,00
20240715094559E  0,50
20240715100042E  1,00
20240715100239E  1,00
20240715100442E  0,50
20240715101139E  2,00
20240715101334.  0,00
20240715102222E  1,00
20240715103538E  0,50
20240715110756E  1,50
20240715110926E  2,00
20240715112242E  0,50
20240715112404Q  0,as
20240715115609E  1,00
20240715125111.  0,00
20240715134613.  0,00
20240715141719E  0,50
20240715142234E  0,50
20240715144859E  2,00
20240716105837.  0,00
20240716111512E  0,60
20240717105021.  0,00
20240717114523.  0,00
20240717124025.  0,00
20240717124940E  1,00
20240719111544.  0,00
20240719111926Q  0,em
20240719112125.  0,00
20240719114343E  0,50
20240721111930.  0,00
20240721112725E  0,50
20240722093113.  0,00
20240722100810E  2,00
20240723110222.  0,00
20240723112026E  2,00
20240724115700.  0,00
20240724124026Q  0,as
20240725114015.  0,00
20240725123517.  0,00
20240726105912.  0,00
20240726111328E  0,50
20240728105155.  0,00
20240728110830E  0,50
20240729173459.  0,00
20240729173958.  0,00
20240729183501.  0,00
20240729193003.  0,00
20240729202505.  0,00
20240729212008.  0,00
20240729221510.  0,00
20240729231012.  0,00
20240730000515.  0,00
20240730010017.  0,00
20240730025021.  0,00
20240730034523.  0,00
20240730044026.  0,00
20240730053528.  0,00
20240730063031.  0,00
20240730072533.  0,00
20240730082035.  0,00
20240730084849Q  0,te
20240730115001.  0,00
20240730115148E  0,50
20240730115322E  1,00
20240730115506.  0,00
20240730120003E  0,60
20240731112209E  1,00
20240731112348E  1,00
20240731112548E  1,00
20240731112800E  1,00
]]


--    text1 = text1 .. "\n" .. trim(text101) .. "\n"      --  zu Testzwecken
    
    local md5key    = md5lib.hash(text1)
    local signature = App.crypt('',sec_key1, md5key)

--    local f = io.open('newreport.txt','w')
    local text2 = signature .. "\n" .. text1 .. "\n"
    my_write_filedata1('newreport.txt',sd_dir..'/newreport.txt',App.crypt('',sec_key3,text2,80),over_write)

--    f:write(text2)
--    f:close()

end

--*************************************************************************
--private

if arg ~= nil and arg[1] == 'tan' then
    translation_table = ""
    function write_flash_string (x) return end
    local x = App:new(1)
    print('-----------------------------------------------')
    x:compute_tan(arg[2])
    print('-----------------------------------------------')
end

--*************************************************************************
--private

if arg ~= nil and arg[1] == 'test1' then
    translation_table = ""
    local x = App:new(1)
    local hh0 = "20220730000001"
    local hh1 = x:time(hh0)
    print (hh0, hh1)
    local hh2 = x:date("%y%m%d%H%M%S",hh1)
    local hh3 = x:time(hh2)
    print (hh2, hh3)
    local hh4 = x:date("%y%m%d%H%M%S",hh3)
    local hh5 = x:time(hh4)
    print (hh4, hh5)
end


--*************************************************************************
--private

if arg ~= nil and arg[1] == 'test2' then
    translation_table = ""
    local x = App:new(1)
    local hh1 = x:date("%y %m %d  %H:%M:%S",tonumber(arg[2]))
    print (hh1)
end


--*************************************************************************
--private

if arg ~= nil and arg[1] == 'test3' then
    translation_table = ""
    local x = App:new(1)

    local hh0  = arg[2]
    local hh1  = x:time(hh0)
    print(hh1)
    print("223344 1XX",hh0)
    local hh2 = x:date(t("%y%m%d%H%M%S"),hh1)
    print("223344 2XX",hh2)

end

--*************************************************************************
--private

if arg ~= nil and arg[1] == 'test4' then
    translation_table = ""
    local x   = App:new(1)
    local erg = text_to_numbers("Dönüş")
    print(erg)
    local erg1 = numbers_to_text(erg)
    print(erg1)
    print("-----")
    
    local erg2 = replace_diacritic_letters("Dönüş")
    print(erg2)

end


--*************************************************************************
--private

if arg ~= nil and arg[1] == 'test5' then
    translation_table = ""
    local x   = App:new(1)
--    local erg = x:crypt(sec_key,"abcjksdhfjvsdflöjsdaöklfjsdöklajföklasdjfökladjsfkölsdjaöklfjdskölafjkdölsajfkölsdafjkölasdjfökldsajfkölasdjfkölasdjöfklsdjakhdsjfhsdjklfhsdjaklfhjdkalhfjdklshafjkdlashfjkldshfjklsdhfjkdlashadhf",80)
    local erg = x:crypt(sec_key3,SERIALNUMBER.."83552".."430".."fdsxjkslafzrue")
    local uvw = x:decrypt(pub_key3,erg)

    print("x-"..erg.."-x")
    print("y-"..uvw.."-y")

end

--*************************************************************************
--private

if arg ~= nil and arg[1] == 'test6' then
    translation_table = ""
    local x   = App:new(1)
    
    print(tonumber("0xfff"))
    
    print( string.format("%02x",3251) )

end


--*************************************************************************
--private


if arg ~= nil and arg[1] == 'tt' then
    translation_table = ""
    local x   = App:new(1)
    local y   = x:date('%H%M',15*3600+20*60)
    print(y)

end


--*************************************************************************
--private

if arg ~= nil and arg[1] == 'read' then
    translation_table = ""
    local x   = App:new(1)
    
    local erg  = my_read_filedata1(arg[2])
    local erg1 = x:decrypt(sec_key3,erg)

    print(erg1)

end


--*************************************************************************
--private

if arg ~= nil and arg[1] == 'write' then
    translation_table = ""
    local x   = App:new(1)
    
    local erg = my_read_filedata1(arg[2])
    local erg1 = x:crypt(pub_key3,erg,80)
    my_write_filedata1(arg[3],arg[3],erg1)

--    print(erg1)

end

--*************************************************************************
--private

if arg ~= nil and arg[1] == 'ty' then
    translation_table = ""
    local x   = App:new(1)
    
    local erg1 = x:crypt(sec_key1,arg[2],80)
    print(erg1)

end


--*************************************************************************
--private

--if arg ~= nil and arg[1] == 'voucher' then
--    translation_table = ""
--    local x    = App:new(1)
--
--    local erg  = my_read_filedata1(arg[2])
--    local erg1 = x:crypt(pub_key1,erg,80)
--    my_write_filedata1("v.c","v.c",trim(erg1).."\n--\n")
--
--    print(erg1)
--
--end


--*************************************************************************
--private

if arg ~= nil and (arg[1] == 'joe' or arg[1] == 'vim' or arg[1] == 'print') then
    translation_table = ""
    local x   = App:new(1)
    
    local erg  = my_read_filedata1(arg[2])
    local erg1 = x:decrypt(sec_key3,erg)
    if arg[1] == 'print' then
        print(erg1)
    else
        my_write_filedata1(arg[2].."~",arg[2].."~",erg1,over_write)

        os.execute(arg[1].." "..arg[2].."~")

        local erg2 = my_read_filedata1(arg[2].."~")
        local erg3 = x:crypt(pub_key3,erg2,80)
        my_write_filedata1(arg[2],arg[2],erg3,over_write)
    end

--    print(erg1)

end

--**********************************************************************
--private

if arg ~= nil and arg[1] == "xpcall" then

    translation_table = ""
    local x = App:new(1)
    
    print_symbols()    

end

--**********************************************************************
--private

if arg ~= nil and arg[1] == "decodeorder" then

    translation_table = ""
    local x = App:new(1)
    html_parameter = arg[2]
    
    local pos = string.sub(html_parameter,1,1)  -- Hineincodieren der serial number modulo 4096
    pos       = "0x" + pos
    pos       = tonumber(pos) + 2
 
    local a1  = string.sub(html_parameter,1,pos)
    local a2  = string.sub(html_parameter,pos+2,pos+2)
    local a3  = string.sub(html_parameter,pos+4,pos+4)
    local a4  = string.sub(html_parameter,pos+6)
    
    local sk1 = string.sub(html_parameter,pos+1,pos+1)
    local sk2 = string.sub(html_parameter,pos+3,pos+3)
    local sk3 = string.sub(html_parameter,pos+5,pos+5)
    
    
    html_parameter = a1 .. a2 .. a3 .. a4

    print( sk1 .. sk2 .. sk3 .. x:decrypt(pub_key1,html_parameter) )

end

--*************************************************************************
--private

if arg ~= nil and arg[1] == "keys" then

--    ak1 = tostring(arg[1])

    bk1 = bin.stohex( crypto.public_key( bin.hextos(ak1) ) )
    bk2 = bin.stohex( crypto.public_key( bin.hextos(ak2) ) )
  
    sec_key2 = bk1 .. ak2
    pub_key2 = bk2 .. ak1
    
    if sec_key ~= sec_key2 or pub_key ~= pub_key2 or arg[2] ~= nil then

        local statf  = io.open('/proc/self/stat')
        math.randomseed(os.time()+os.clock()+tonumber(string.sub(statf:read(),1,4)))
        statf:close()

        ak1 = {}
        ak2 = {}
        for i = 1,256,1 do
--            ak1 = ak1 .. tostring(math.random(0,1))
--            ak2 = ak2 .. tostring(math.random(0,1))
            table.insert(ak1,tostring(math.random(0,1)))
            table.insert(ak2,tostring(math.random(0,1)))
        end
        ak1 = bin.bintohex(ak1)
        ak2 = bin.bintohex(ak2)

        bk1 = bin.stohex( crypto.public_key( bin.hextos(ak1) ) )
        bk2 = bin.stohex( crypto.public_key( bin.hextos(ak2) ) )
  
        sec_key = bk1 .. ak2
        pub_key = bk2 .. ak1

        fc = io.open('newkeys.txt','w')
        if fc ~= nil then
            fc:write('\n')
            fc:write('sec_key = \''..sec_key..'\'\n')
            fc:write('pub_key = \''..pub_key..'\'\n')
            fc:write('ak1     = \''..ak1..'\'\n')
            fc:write('ak2     = \''..ak2..'\'\n')
            fc:close()
        end
    else
        os.remove('newkeys.txt')
    end

end


--*************************************************************************
--private

if arg ~= nil and arg[2] == "rcode" then

    text1 = my_read_filedata1(arg[3],sd_dir..'/'..arg[3])

    text2  = {}
    zeile0 = ""
    for zeile in string.gmatch(text1,'[^\n]+') do

        zeile  = string.gsub(zeile," ","")

        if #zeile == 3 and string.find(zeile,"%d%d%d") then
            a = 1
            zeile0 = ""

        elseif #zeile0 < 27 then
            zeile = zeile0 .. zeile
            zeile0 = zeile

        elseif zeile == "" then

            zaehler = 0
            for zeile in text2 do
            
                zaehler = zaehler + 1
                if text2 == "" then
                    break
                end
                zeile = string.sub(text2,1,27)
                text2 = string.sub(zeile,28)
                t2    = bin.stohex( md5lib.has( string.sub(zeile,1,25) ) )
                t2    = tonumber("0x"..string.sub(t2,1,3)) % 1000
                nr2   = 1 + math.floor(t2/32)
                nr3   = 1 + t2 % 32
                if string.sub(zeile,26,27) == string.sub(CHAR32,nr2,nr2) .. string.sub(CHAR32,nr3,nr3) then
                    print(zeile)
                else
                    print(zeile,"<--")
                end
            
            end
            text2  = {}
            zeile0 = ""
            
        else
         
           table.insert(text2,zeile)
           
       end
                
    end        

end


--*************************************************************************
--private

if arg ~= nil and arg[1] == "pruefcode" then

    translation_table = ""
    local x           = App:new(1)
    x.akt_time        = arg[2]
    x.amount_in_euro  = arg[3]
    x.ticketnr        = arg[4]
    x:voucher_barcode()

    local y = x.transmit_nr
    print(string.sub(y,1,4) .. " " .. string.sub(y,5,8) .. " " .. string.sub(y,9))
    
--    x:voucher_barcode_reverse()
    
end

--*************************************************************************
--private

if arg ~= nil and arg[1] == "sernomod" then

--    ak1 = tostring(arg[1])

    print (math.floor(SERIALNUMBERMOD4096 % tonumber(arg[2]) ))
    
    
end

--******************************************
--private

if arg ~= nil and arg[1] == "test73" then

    e = bin.hextos(arg[2])
    print(#arg[2],#e,e)
    
end



--*************************************************************************
--private

if arg ~= nil and arg[1] == "read_patch" then
    function start_timer (a) if a == 12 then load_parts() end end
    function load() print(PATCH_plaintext) end
    function patch_call ()  end
    function stop_timer ()  end
    function set_value ()   end
    function set_text ()    end
    local jj = load_patch("patch_" .. SERIALNUMBER .. ".enc")
end

--*************************************************************************
--private

if arg ~= nil and arg[1] == "ttime" then

    zaehler = 1
    st      = 1751866900
    math.randomseed(5876)
    while zaehler < 200 do
        st = st + math.random(1000,5000)
        print(math.random(20,60))
        cu = math.random(1,10)
        cu = string.sub("EQX.......",cu,cu)
        print(App.date('',"%y%m%d%H%M%S",st) .. cu .. "  "  .. string.format("%3.2f",math.random(2,60)/10))
        zaehler = zaehler + 1
    end


end
        
--*************************************************************************
--private

if arg ~= nil and arg[1] == "signatable" then   --  Ausdruck der Signature Lookup Table

    local x = App:new(1)
    
    print( create_signa_table( tonumber(arg[2]),120 ))
    if arg[3] == "1" then
        print(VOUCHER_M,PRINT_BF_EXIST)
    end
    
end

--*************************************************************************
--private

if arg ~= nil and arg[1] == "report" then   --  manuelles Erstellen eines Ticket Report


    translation_table = ""
    local x           = App:new(1)
    function start_timer ()  end
    function flush_flash ()  end
--    function App.write   ()  end
    
    x.squeeze_out     = 1
    x.extern_reportnr = 5
    x.extern_report   = {}
    x.extern_report["reports"] = {}
--    x.erzeugungsinfo  = "abcde"
    for zeile in string.gmatch(my_read_filedata1(arg[2]),'[^\n]+') do
        table.insert(x.extern_report["reports"],zeile)
    end
    
    x.extern_report['header']   = {}
    table.insert(x.extern_report['header'],"Dies ist ein Test")
    x.extern_report['reportnr'] = "004"
    x.extern_report['datum']    = "08.07.2024"
    x.extern_report['zeit']     = "12:29"
    x.extern_report['who']      = "12:29"
    x.extern_report['ust']      = "19.1"
    x.extern_report['stitle']   = "Signatur   "
    x.extern_report['dformat']  = "%d.%m.%y"
    x.extern_report['zformat']  = "%H:%M"
    
    
--    print(#x.extern_report)    

    x:p_generate_report()
    
end

--*************************************************************************
--private

if arg ~= nil and arg[1] == "euklid" then   --  manuelles Erstellen eines Ticket Report


    translation_table = ""
    local x           = App:new(1)
    print(x:euklid(tonumber(arg[2]),tonumber(arg[3])))
    
end

--*************************************************************************
--private


if arg ~= nil and arg[1] == "test9" then

    xx  = string.format("%1x",tonumber(arg[2]))
    print(xx)
    xx1 = bin.hextos(xx)
    print("LL",#xx1)
    xx2 = base32.encode(xx1)
    print(xx,xx2)
    
end

--************************************************************************
--private

if arg ~= nil and arg[1] == "tt5" then

    v,a = custom_voucher (tonumber(arg[2]),tonumber(arg[3]))
    print(a)
    
end

--************************************************************************
--private

if arg ~= nil and arg[1] == "bm" then

    local gg,hh = qrc.qrcode("FGDHJskaDGDKGGdhksgadhkjgsHgsafasdafdafdasJKGSJHKAgfffffkfgkhfgkfgkfgdghksjaGDHKASGdhksgDGSdjhkgshakDGSHKdgjhksaDGHKSdghkjsgaDHKJGkghasjkDGHKSgdhkjasgdhksjaKTR6543",4)

    for i,x in ipairs(hh) do
    x1 = ""
    for j,y in ipairs(x) do
        if y > 0 then
            x1 = x1 .. "XX"
        else
            x1 = x1 .. "  "
        end
    end
    print(x1)
end

    local ff,vv  = App.qr_to_bitmap('',2,hh,9)

    local qr_s   = App.qrcode_to_string('',hh)
--    local qr_hex = bin.stohex(qr_s)
    local qr_27  = base27_encode(qr_s)
--    print(qr_hex)
--    print(qr_27)

    local erg = qr_27
    local zeilenlaenge = 32
    local padding = #erg % zeilenlaenge
    if padding > 0 then
        erg = erg .. string.rep("E",zeilenlaenge-padding)
    end

    local erg1 = ""
    while zeilenlaenge ~= nil do
        erg1 = erg1 .. string.sub(erg,1,zeilenlaenge) .. "\n"
        erg  = string.sub(erg,zeilenlaenge+1)
        if #erg == 0 then
            break
        end
    end
    print(erg1.."\n",#erg1/33,#hh)


--for i=1,#vv do
--    a,b = string.gsub(string.gsub(vv[i],"0"," "),"1","X")
--    print(a)
--end

    
end


--************************************************************************
--private


if arg ~= nil and arg[1] == "zip1" then


    text3 = [[
    
12park
0553227 / 009
12.07.2024 08:44:32
 
09.07.2024        17:50   1,40 E
--++=-----               -------
09:18   0,bo Q           13,90 E
09:42   0,te Q         
09:42   0,ka Q    11.07.2024
10:07   0,ka Q    ----------
10:10   0,50 E    08:39   0,bo Q
10:28   1,00 E    08:52   0,bo Q
10:28   2,00 E    08:57   0,ka Q
10:41   0,50 E    09:04   2,20 E
10:55   0,ka Q    09:09   0,te Q
11:06   1,50 E    10:03   1,00 E
11:08   2,00 E    10:16   0,ka Q
11:35   2,00 E    10:40   2,00 E
15:20   0,50 E    10:47   0,50 E
15:40   0,60 E    10:53   1,30 E
15:40   0,bo Q    11:03   1,50 E
16:19   0,50 E    11:03   2,00 E
19:38   4,00 E    11:14   0,50 E
19:52   3,70 E    11:20   1,00 E
       -------    11:23   0,50 E
       18,80 E    12:08   0,50 E
                  12:08   0,em Q
10.07.2024        12:08   0,em Q
----------        12:22   2,00 E
07:59   1,00 E    12:31   0,50 E
08:40   0,te Q    12:48   0,bo Q
08:41   0,te Q    14:02   0,50 E
09:03   0,bo Q    14:14   0,ka Q
09:15   0,em Q    14:46   2,00 E
09:15   0,em Q    15:04   1,00 E
09:30   0,50 E    15:18   1,00 E
09:53   1,00 E    15:27   2,00 E
10:10   2,00 E    15:53   1,50 E
10:12   1,00 E    17:00   2,00 E
10:24   1,00 E           -------
11:20   1,00 E           25,50 E
11:57   1,00 E         
12:01   0,50 E    NETTO:   48,91
12:53   0,bo Q    USt19%    9,29
12:56   0,ka Q    TOTAL:   58,20
14:21   1,50 E    
14:52   1,00 E    
16:28   1,00 E    
 
www.12park.de

]]

    text4 = App.make_signa_report(App,text3,"",5)
    
    print(text4)
    
end


--************************************************************************
--private

if arg ~= nil and arg[1] == 'test704' then
    

    math.randomseed(tonumber(os.time()%875647))
    
    rr = math.random(1,100010001000100) 
    rr1 = string.format("%0x",rr)
    
    rr2 = table.concat( bin.hextobin(string.sub(rr1,3)) )

    rr = math.random(1,100010001000100) 
    rr1 = string.format("%0x",rr)
    rr2 = rr2 .. table.concat( bin.hextobin(string.sub(rr1,3)) )

    text1 = rr2
    print(text1)
    text2 = base62_encode(text1)
    print(text2,#text2)

--    text2 = "10"

    text3 = base62_decode(text2)
    print(text3)

    if pad_down(text1,"0") == text3 then
        print("OK")
    end
    
end

    
--************************************************************************
--private


if arg ~= nil and arg[1] == "qrenc" then

text = 
"fdfdlkafhldahfjkldhafkldhajlfhdjlsfhjdshlfjdhlasfjdhlajfhdjalfhttttttttffffffff4444" ..
"fdfdlkafhldahfjkldhafkldhajlfhdjlsfhjdshlfjdhlasfjdhlajfhdjalfhttttttttffffffff4444"


text1 = App.crypt('',sec_key1,text,32)


print(text1)

end


if arg ~= nil and arg[1] == "ttt" then

    local text6        = [[
    
    dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
    dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
    dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
    dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
    dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
    dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
    dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
    dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
    dsadfadsaDSAdsaDSADAFGDJKFAgjdfsgFDDKFÖDAGJFÄDAJKÄFGJAFGÄKRJWÄKGJFKÄSJGÄVKÄLJVKÄRMVKÄMKÄLNäjsdkvädaävndkaälvnfklaänvklfäanvaä
    
]]    
    
    text6 = " "
    
    print(text6)


    local text7        = base27_encode(text6)
    print(text7)
    local text8        = base27_decode(text7)
    print(text8)

end

--**********************************************************************
--  CONSOLE_AREA_END
--**********************************************************************
--  TRANSLATIONS_BEGIN
--**********************************************************************


translation_table = [[

;LCODE;DE;EN;FR;IT;DE;ES;PT;NL;FR;FL;TR;PL;RO;DE;FR;IT;CS;SK;HR;
;LANG;Deutsch;English;Français;Italiano;Deutsch;Español;Português;Nederlands;Français;Flaams;Türkçe;Polski;Română;Deutsch;Français;Italiano;Čeština;Slovenská;Hrvatski;
;S2F270;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Inserire le monete;Bitte Münzen einwerfen;Por favor inserte;Por favor, insira moedas;Gelieve munten;Veuillez insérer des;Gelieve munten;Lütfen bozuk para girin;Proszę włożyć monety;Vă rugăm să;Bitte Münzen einwerfen;Veuillez insérer des;Inserire le monete;Vložte prosím mince;Vložte mince;Ubacite kovanice;
;S2F271;-;-;pièces;-;-;monedas;-;in te voeren;pièces;in te voeren;-;-;introduceți monede;-;pièces;-;-;-;-;
;S2F171;Bargeldlos bezahlen:;Cashless payment:;Payer sans argent liquide:;Pagamento senza contanti:;Bargeldlos bezahlen:;Pago sin efectivo:;Pagamentos sem dinheiro:;Contant betalen:;Payer sans argent liquide:;Contant betalen:;Nakitsiz ödemeler:;Płatności bezgotówkowe:;Plăți fără numerar:;Bargeldlos bezahlen:;Payer sans argent liquide:;Pagamento senza contanti:;Bezhotovostní platby:;Bezhotovostné platby:;Bezgotovinsko plaćanje;
;S3F171;Bargeldlos bezahlen:;Cashless payment:;Payer sans argent liquide:;Pagamento senza contanti:;Bargeldlos bezahlen:;Pago sin efectivo:;Pagamentos sem dinheiro:;Contant betalen:;Payer sans argent liquide:;Contant betalen:;Nakitsiz ödemeler:;Płatności bezgotówkowe:;Plăți fără numerar:;Bargeldlos bezahlen:;Payer sans argent liquide:;Pagamento senza contanti:;Bezhotovostní platby:;Bezhotovostné platby:;Bezgotovinsko plaćanje;
;S15F171;Bargeldlos bezahlen:;Cashless payment:;Payer sans argent liquide:;Pagamento senza contanti:;Bargeldlos bezahlen:;Pago sin efectivo:;Pagamentos sem dinheiro:;Contant betalen:;Payer sans argent liquide:;Contant betalen:;Nakitsiz ödemeler:;Płatności bezgotówkowe:;Plăți fără numerar:;Bargeldlos bezahlen:;Payer sans argent liquide:;Pagamento senza contanti:;Bezhotovostní platby:;Bezhotovostné platby:;Bezgotovinsko plaćanje;
;S3F271;Parkzeit;Parking time;Temps de stationnement;Tempo di parcheggio;Parkzeit;Tiempo de estacionamiento;Tempo de estacionamento;Parkeertijd;Temps de stationnement;Parkeertijd;Park etme zamanı;Czas parkowania;Timp de parcare;Parkzeit;Temps de stationnement;Tempo di parcheggio;Doba parkování;Čas parkovania;Trajanje parkiranja;
;A3F271;Anzahl Tickets;Total count of tickets;Nombre total de billets;Numero totale di biglietti;Anzahl Tickets;Recuento total de tickets;Contagem total de bilhetes;Totaal aantal tickets;Nombre total de billets;Totaal aantal tickets;Toplam bilet sayısı;Łączna liczba biletów;Numărul total de bilete;Anzahl Tickets;Nombre total de billets;Numero totale di biglietti;Celkový počet vstupenek;Skupno število vstopnic;Ukupan broj ulaznica;
;S3F270;Für Ticket Knopf drücken;Press button for ticket;Appuyez sur le bouton;Premere il pulsante per il biglietto;Für Ticket Knopf drücken;Presione el botón;Pressione o botão;Druk op de knop voor ticket;Appuyez sur le bouton;Druk op de knop voor ticket;Bilet için butona basın;Naciśnij przycisk,;Apăsați butonul;Für Ticket Knopf drücken;Appuyez sur le bouton;Premere il pulsante per il biglietto;Stiskněte tlačítko pro jízdenku;Stlačte tlačidlo pre lístok;Za kartu,;
;S3F277;-;-;pour le billet;-;-;para el boleto;para o bilhete;-;pour le billet;-;-;aby otrzymać bilet;pentru bilet;-;pour le billet;-;pro jízdenku;-;pritisnite gumb;
;S3F204;Nochmal;Again;Encore;Ancora;Nochmal;De nuevo;De novo;Opnieuw;Encore;Opnieuw;Tekrar;Ponownie;Din nou;Nochmal;Encore;Ancora;Zvonu;Znova;Opet;
;S3F278;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Inserire le monete;Bitte Münzen einwerfen;Por favor inserte;Por favor, insira moedas;Gelieve munten;Veuillez insérer des;Gelieve munten;Lütfen bozuk para girin;Proszę włożyć monety;Vă rugăm să;Bitte Münzen einwerfen;Veuillez insérer des;Inserire le monete;Vložte prosím mince;Vložte mince;Ubacite kovanice;
;S3F279;-;-;pièces;-;-;monedas;-;in te voeren;pièces;in te voeren;-;-;introduceți monede;-;pièces;-;-;-;-;
;BITTEMUENZENA;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Inserire le monete;Bitte Münzen einwerfen;Por favor inserte;Por favor, insira moedas;Gelieve munten;Veuillez insérer des;Gelieve munten;Lütfen bozuk para girin;Proszę włożyć monety;Vă rugăm să;Bitte Münzen einwerfen;Veuillez insérer des;Inserire le monete;Vložte prosím mince;Vložte mince;Ubacite kovanice;
;BITTEMUENZENB;-;-;pièces;-;-;monedas;-;in te voeren;pièces;in te voeren;-;-;introduceți monede;-;pièces;-;-;-;-;
;KNOPFDRUECKENA;Für Ticket Knopf drücken;Press button for ticket;Appuyez sur le bouton;Premere il pulsante per il biglietto;Für Ticket Knopf drücken;Presione el botón;Pressione o botão;Druk op de knop voor ticket;Appuyez sur le bouton;Druk op de knop voor ticket;Bilet için butona basın;Naciśnij przycisk,;Apăsați butonul;Für Ticket Knopf drücken;Appuyez sur le bouton;Premere il pulsante per il biglietto;Stiskněte tlačítko pro jízdenku;Stlačte tlačidlo pre lístok;Za kartu,;
;KNOPFDRUECKENB;-;-;pour le billet;-;-;para el boleto;para o bilhete;-;pour le billet;-;-;aby otrzymać bilet;pentru bilet;-;pour le billet;-;pro jízdenku;-;pritisnite gumb;
;S3F267;Ticket wird gedruckt;Ticket is printed;Le billet est imprimé;Il biglietto è stampato;Ticket wird gedruckt;Se imprime el boleto;O bilhete é impresso;Ticket is afgedrukt;Le billet est imprimé;Ticket is afgedrukt;Bilet basıldı;Bilet jest drukowany;Biletul este tipărit;Ticket wird gedruckt;Le billet est imprimé;Il biglietto è stampato;Lístek je vytištěn;Lístok je vytlačený;Karta se ispisuje;
;S3F201;Sondertarif;Special rate;Tarif spécial;Tasso speciale;Sondertarif;Tarifa especial;Tarifa especial;Speciaal tarief;Tarif spécial;Speciaal tarief;Özel tarife;Taryfa specjalna;Tarif special;Sondertarif;Tarif spécial;Tasso speciale;Speciální sazba;Špeciálna tarifa;Posebna stopa;
;S5F217;Hauptmenü;Main menu;Menu principal;Menu principale;Hauptmenü;Menú principal;Menu principal;Hoofdmenu;Menu principal;Hoofdmenu;Ana menü;Menu główne;Meniu principal;Hauptmenü;Menu principal;Menu principale;Hlavní menu;Hlavné menu;Glavni izbornik;
;S5F202;Lokale Einstellungen;Local settings;Paramètres locaux;Impostazioni locali;Lokale Einstellungen;Configuraciones locales;Configurações locais;Lokale instellingen;Paramètres locaux;Lokale instellingen;Yerel ayarlar;Ustawienia lokalne;Setari locale;Lokale Einstellungen;Paramètres locaux;Impostazioni locali;Místní nastavení;Miestne nastavenia;Lokalne postavke;
;S5F201;Verfügbare Punkte:;Available points:;Points disponibles:;Punti disponibili:;Verfügbare Punkte:;Puntos disponibles:;Pontos disponíveis:;Beschikbare punten:;Points disponibles:;Beschikbare punten:;Mevcut noktalar:;Dostępne punkty:;Puncte disponibile:;Verfügbare Punkte:;Points disponibles:;Punti disponibili:;Dostupné body:;Dostupné body:;Raspoloživi bodovi;
;S5F212;Tarif;Tariff;Tarif;Tariffa;Tarif;Tarifa;Tarifa;Tarief;Tarif;Tarief;Tarife;Taryfa;Tarifar;Tarif;Tarif;Tariffa;Tarif;Tarifa;Tarifa;
;S5F213;Protokoll;Protocol;Protocole;Protocollo;Protokoll;Protocolo;Protocolo;Protocol;Protocole;Protocol;Protokol;Protokół;Protocol;Protokoll;Protocole;Protocollo;Protokol;Protokol;Protokol;
;S5F205;Erweitert;Extended;Additionnel;Esteso;Erweitert;Extendido;Avançado;Verlengd;Additionnel;Verlengd;Ileri;Zaawansowane;Avansat;Erweitert;Additionnel;Esteso;Pokročilý;Pokročilé;Produljenje;
;S5F299;Beenden;Finish;Rompre;Fine;Beenden;Finalizar;Terminar;Af hebben;Rompre;Af hebben;Son;Koniec;Sfârşit;Beenden;Rompre;Fine;Konec;Koniec;Kraj;
;S5F204;Tickets seit Neustart:;Tickets since restart:;Billets depuis redémarrage:;Biglietti dal riavvio:;Tickets seit Neustart:;Entradas desde el reinicio:;Bilhetes desde o reinício:;Tickets sinds herstart:;Billets depuis redémarrage:;Tickets sinds herstart:;Yeniden başlatmadan bu yana biletler:;Bilety od restartu:;Bilete de la repornire:;Tickets seit Neustart:;Billets depuis redémarrage:;Biglietti dal riavvio:;Vstupenky od restartu:;Lístky od reštartu:;Karte prije ponovnog pokretanja;
;S6F203;Mindestbetrag;Minimum amount;Montant minimal;Quantità minima;Mindestbetrag;Monto minimo;Valor mínimo;Minimale hoeveelheid;Montant minimal;Minimale hoeveelheid;Minimum miktar;Stawka minimalna;Cantitatea minima;Mindestbetrag;Montant minimal;Quantità minima;Minimální množství;Minimálna suma;Minimalan iznos;
;S6F208;Preis pro Stunde;Price per hour;Prix par heure;Prezzo all'ora;Preis pro Stunde;Precio por hora;Preço por hora;Prijs per uur;Prix par heure;Prijs per uur;Saatlik fiyat;Cena za godzinę;Pret pe ora;Preis pro Stunde;Prix par heure;Prezzo all'ora;Cena za hodinu;Cena za hodinu;Cjena po satu;
;S6F201;Mehrwertsteuer %;VAT %;T.V.A. %;IVA %;Mehrwertsteuer %;IVA %;Imposto Valor Acr. %;BTW %;T.V.A. %;BTW %;Katma değer Vergisi %;Podatek obrotowy %;Taxa pe valoare adaugata %;Mehrwertsteuer %;T.V.A. %;IVA %;Daň z přidané hodnoty %;Daň z pridanej hodnoty %;PDV %;
;S6F204;Tarif;Tariff;Tarif;Tariffa;Tarif;Tarifa;Tarifa;Tarief;Tarif;Tarief;Tarife;Taryfa;Tarifar;Tarif;Tarif;Tariffa;Tarif;Tarifa;Tarifa;
;S6F207;Höchstparkdauer;Maximum parking;Maximale de;Parcheggio;Höchstparkdauer;Estacionamiento;Tempo máx. estacion.;Maximaal parkeren;Maximale de;Maximaal parkeren;Maksimum park süresi;Maksymalny czas;Timp maxim de;Höchstparkdauer;Maximale de;Parcheggio;Maximální doba parkování;Maximálny čas parkovania;Maksimalno trajanje;
;S6F206;-;-;stationnement;massimo;-;máximo;-;-;stationnement;-;-;parkowania;parcare;-;stationnement;massimo;-;-;parkinga;
;S6F211;Tagesticket;Day ticket;Billet journalier;Biglietto giornaliero;Tagesticket;Boleto de un día;Bilhete diário;Dagkaart;Billet journalier;Dagkaart;Günlük bilet;Bilet dzienny;Bilet de zi;Tagesticket;Billet journalier;Biglietto giornaliero;Denní jízdenka;Denný lístok;Dnevna karta;
;S6F215;Stunden;Hours;Heures;Ore;Stunden;Horas;Horas;Uren;Heures;Uren;Saat;Godziny;Ore;Stunden;Heures;Ore;Hodin;Hodiny;Sati;
;S6F216;Minuten;Minutes;Minutes;Minuti;Minuten;Minutos;Minutos;Minuten;Minutes;Minuten;Dakika;Minuty;Minute;Minuten;Minutes;Minuti;Minut;Minút;Minute;
;S6F217;Nein;No;Non;No;Nein;No;Não;Nee;Non;Nee;Hayır;Nie;Nu;Nein;Non;No;Ne;Nie;Ne;
;S6F218;Ja;Yes;Oui;Sì;Ja;Sí;Sim;Ja;Oui;Ja;Evet;Tak;Da;Ja;Oui;Sì;Ano;Áno;Da;
;S6F220;Münzen;Coins;Monnaie;Monete;Münzen;Monedas;Moedas;Munten;Monnaie;Munten;Madeni paralar;Monety;Monede;Münzen;Monnaie;Monete;Mince;Mince;Kovanice;
;S6F221;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QR kod;
;S6F222;Mü+QR;Co+QR;Mo+QR;Mo+QR;Mü+QR;Mo+QR;Mo+QR;Mu+QR;Mo+QR;Mu+QR;Ma+QR;Mo+QR;Mo+QR;Mü+QR;Mo+QR;Mo+QR;Mi+QR;Mi+QR;Ko+QR;
;S6F233;Zahlungsweise;Payment mode;Mode de paier;Metodo di pagare;Zahlungsweise;Modo de pago;Forma de pagar;Betaalmethode;Mode de paier;Betaalmethode;Ödeme şekli;Metoda płatności;Metodă de plată;Zahlungsweise;Mode de paier;Metodo di pagare;Způsob platby;Spôsob platby;Način plaćanja;
;S6F240;Tabelle;Table;Tableau;Tavolo;Tabelle;Tabla;Mesa;Tabel;Tableau;Tabel;Tabel;Tabela;Tabel;Tabelle;Tableau;Tavolo;Tabulka;Tabuľka;Tabela;
;S6F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S6F219;Weiter zu Tarif 2;Go to tariff 2;Aller au tarif 2;Vai alla tariffa 2;Weiter zu Tarif 2;Ir a la tarifa 2;Continuar Tarifa 2;Ga naar tarief 2;Aller au tarif 2;Ga naar tarief 2;Tarife 2 ayarları;Przejdź do taryfy 2;Continuați la Tariful 2;Weiter zu Tarif 2;Aller au tarif 2;Vai alla tariffa 2;Pokračujte na tarif 2;Prejsť na tarifu 2;Izaberi tarifu 2;
;S7F204;Erweitert;Extended;Additionnel;Esteso;Erweitert;Extendido;Avançado;Verlengd;Additionnel;Verlengd;Ileri;Zaawansowane;Avansat;Erweitert;Additionnel;Esteso;Pokročilý;Pokročilé;Produljenje;
;S7F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S7F201;TAN Freigabe für Tickets;Charging TAN for tickets;TAN de recharge des billets;Addebito TAN per i biglietti;TAN Freigabe für Tickets;Cobro de TAN por boletos;Recarga TAN para bilhetes;TAN opladen voor tickets;TAN de recharge des billets;TAN opladen voor tickets;Biletler için TAN sürümü;Wydanie TAN dla biletów;Eliberare TAN pentru bilete;TAN Freigabe für Tickets;TAN de recharge des billets;Addebito TAN per i biglietti;TAN uvolnění pro vstupenky;Uvoľnenie TAN na lístky;Naplaćivanje TAN broja za karte;
;S7F212;PIN Verwaltung;PIN Administration;Administration PIN;Amministrazione PIN;PIN Verwaltung;Administración de PIN;Gestão de PIN;PIN beheer;Administration PIN;PIN beheer;PIN yönetimi;Zarządzanie kodami PIN;Management PIN;PIN Verwaltung;Administration PIN;Amministrazione PIN;Správa PIN;Správa PIN;PIN izbornik;
;S7F203;Neue PIN Liste;New PIN list;Nouvelle liste des codes PIN;Nuovo elenco PIN;Neue PIN Liste;Nueva lista de PIN;Nova lista PIN;Nieuwe PIN lijst;Nouvelle liste des codes PIN;Nieuwe PIN lijst;Yeni PIN listesi;Nowa lista PIN;Listă PIN nouă;Neue PIN Liste;Nouvelle liste des codes PIN;Nuovo elenco PIN;Nový seznam PIN;Nový zoznam PIN;Nova PIN lista;
;S7F208;Gerät;Device;Appareil;Dispositivo;Gerät;Dispositivo;Dispositivo;Apparaat;Appareil;Apparaat;Cihaz;Urządzenie;Dispozitiv;Gerät;Appareil;Dispositivo;Přístroj;Zariadenie;Uređaj;
;S8F202;Sprache;Language;Langue;Lingua;Sprache;Idioma;Língua;Taal;Langue;Taal;Dil;Język;Limba;Sprache;Langue;Lingua;Jazyk;Jazyk;Jezik;
;S8F203;Standort 1;Location 1;Emplacement 1;Posizione 1;Standort 1;Ubicación 1;Local 1;Locatie 1;Emplacement 1;Locatie 1;Konum 1;Lokalizacja 1;Locația 1;Standort 1;Emplacement 1;Posizione 1;Umístění 1;Umiestnenie 1;Lokacija 1;
;S8F204;Lokale Einstellungen;Local settings;Paramètres locaux;Impostazioni locali;Lokale Einstellungen;Configuraciones locales;Configurações locais;Lokale instellingen;Paramètres locaux;Lokale instellingen;Yerel ayarlar;Ustawienia lokalne;Setari locale;Lokale Einstellungen;Paramètres locaux;Impostazioni locali;Místní nastavení;Miestne nastavenia;Lokalne postavke;
;S8F207;Datum;Date;Date;Data;Datum;Fecha;Encontro;Datum;Date;Datum;Tarih;Data;Data;Datum;Date;Data;Datum;Dátum;Datum;
;S8F206;Zeit;Time;Temps;Volta;Zeit;Tiempo;Tempo;Tijd;Temps;Tijd;Zaman;Czas;Timp;Zeit;Temps;Volta;Čas;Čas;Vrijeme;
;S8F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S8F205;Standort 2;Location 2;Emplacement 2;Posizione 2;Standort 2;Ubicación 2;Local 2;Locatie 2;Emplacement 2;Locatie 2;Konum 2;Lokalizacja 2;Locația 2;Standort 2;Emplacement 2;Posizione 2;Umístění 2;Umiestnenie 2;Lokacija 2;
;S8F211;Standort 3;Location 3;Emplacement 3;Posizione 3;Standort 3;Ubicación 3;Local 3;Locatie 3;Emplacement 3;Locatie 3;Konum 3;Lokalizacja 3;Locația 3;Standort 3;Emplacement 3;Posizione 3;Umístění 3;Umiestnenie 3;Lokacija 3;
;S8F215;Protokolle:;Protocols:;Protocoles:;Protocoll:;Protokolle:;Protocolos:;Protocolos:;Protocollen:;Protocoles:;Protocollen:;Kütükler:;Protokoły:;Protocoalele:;Protokolle:;Protocoles:;Protocoll:;Protokoly:;Protokoly:;Protokol;
;S8F216;DE;EN;FR;IT;DE;ES;PT;NL;FR;NL;TR;PL;RO;DE;FR;IT;CS;SK;HR;
;S9F213;Drucken...;Print...;Imprimer...;Stampa...;Drucken...;Impresión...;Imprimir...;Afdrukken...;Imprimer...;Afdrukken...;Yazdır...;Nacisnąć...;Imprima...;Drucken...;Imprimer...;Stampa...;Tlačit...;Stlačiť...;Ispisivanje...;
;S9F204;Protokoll;Protocol;Protocole;Protocollo;Protokoll;Protocolo;Protocolo;Protocol;Protocole;Protocol;Protokol;protokół;Protocol;Protokoll;Protocole;Protocollo;Protokol;Protokol;Protokol;
;S9F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S9F203;Zeilen pro Seite;Lines per page;Lignes par page;Righe per pagina;Zeilen pro Seite;Líneas por página;Linhas por página;Regels per pagina;Lignes par page;Regels per pagina;Sayfa başına satır;Wierszy na stronę;Linii pe pagină;Zeilen pro Seite;Lignes par page;Righe per pagina;Řádků na stránku;Riadkov na stránku;Linije po stranici;
;S9F202;Protokollzähler:;Protocol counter:;Compteur de journaux:;Contatore protocollo:;Protokollzähler:;Contador de protocolo:;Contador protoc.:;Protocolteller:;Compteur de journaux:;Protocolteller:;Günlük sayacı:;Licznik protokołów:;Contor de jurnal:;Protokollzähler:;Compteur de journaux:;Contatore protocollo:;Počítadlo protokolu:;Počítadlo denníka:;Brojač protokola;
;S9F205;Report wird erstellt...;Ticket is created...;Le report est creé...;Il biglietto è stato creato...;Report wird erstellt...;Se crea el ticket...;O relatório é criado...;Ticket is aangemaakt...;Le report est creé...;Ticket is aangemaakt...;Rapor oluşturuluyor...;Raport jest tworzony...;Raportul este creat...;Report wird erstellt...;Le report est creé...;Il biglietto è stato creato...;Sestava se vytváří...;Správa sa vytvára...;Karta se kreira...;
;S10F270;Gerät nicht betriebsbereit;Device not ready;Appareil non en service;Dispositivo non pronto;Gerät nicht betriebsbereit;Dispositivo no listo;Dispositivo não está pronto;Apparaat niet gereed;Appareil non en service;Apparaat niet gereed;Cihaz çalışmaya hazır değil;Urządzenie nie jest gotowe do pracy;Aparatul nu este pregătit pentru funcționare;Gerät nicht betriebsbereit;Appareil non en service;Dispositivo non pronto;Zařízení není připraveno k provozu;Zariadenie nie je pripravené;Uređaj nije spreman;
;S11F280;Sprachauswahl;Language selection;Sélection de la langue;Selezione della lingua;Sprachauswahl;Selección de idioma;Seleção de idioma;Taal selectie;Sélection de la langue;Taal selectie;Dil seçimi;Wybór języka;Alegerea limbii;Sprachauswahl;Sélection de la langue;Selezione della lingua;Výběr jazyka;Výber jazyka;Odabir jezika;
;S11F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S12F280;PIN Eingabe;PIN entry;Entrée PIN;Inserimento PIN;PIN Eingabe;Entrada de PIN;Entrada PIN;PIN invoer;Entrée PIN;PIN invoer;Pin girişi;PIN wpis;Intrare PIN;PIN Eingabe;Entrée PIN;Inserimento PIN;Zadání PIN;Zadanie PIN;Unos pina;
;S12F202;Ungültige PIN;Invalid PIN;Code PIN invalide;PIN non valido;Ungültige PIN;PIN no válido;PIN inválido;Ongeldige pincode;Code PIN invalide;Ongeldige pincode;Geçersiz PIN;Nieprawidłowy PIN;PIN nevalid;Ungültige PIN;Code PIN invalide;PIN non valido;Neplatný PIN;Neplatný PIN;Neispravan PIN;
;S14F280;TAN Freigabe;TAN validation;TAN validation;Convalida TAN;TAN Freigabe;validación TAN;Liberação TAN;TAN-validatie;TAN validation;TAN-validatie;TAN sürümü;Wydanie TAN;Eliberare TAN;TAN Freigabe;TAN validation;Convalida TAN;Uvolnění TAN;Uvoľnenie TAN;Potvrda TAN broja;
;S14F217;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S14F207;TAN anfordern;Order a TAN;Commander un TAN;Ordina un TAN;TAN anfordern;Pide un TAN;Solicitar TAN;Bestel een TAN;Commander un TAN;Bestel een TAN;TAN talep et;Poproś o TAN;Solicitați TAN;TAN anfordern;Commander un TAN;Ordina un TAN;Vyžádejte si TAN;TAN objednávka;Zatraži TAN broj;
;S15F270;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Si prega di;Bitte Münzen einwerfen;Por favor inserte;Por favor, insira moedas;Gelieve munten;Veuillez insérer des;Gelieve munten;Lütfen bozuk para girin;Proszę włożyć monety;Vă rugăm să;Bitte Münzen einwerfen;Veuillez insérer des;Si prega di;Vložte prosím mince;Vložte mince;Molimo umetnite kovanice;
;S15F271;-;-;pièces;inserire monete;-;monedas;-;in te voeren;pièces;in te voeren;-;-;introduceți monede;-;pièces;inserire monete;-;-;-;
;S16F270;Bitte Münzen einwerfen;Please insert coins;Veuillez insérer des;Si prega di;Bitte Münzen einwerfen;Por favor inserte;Por favor, insira moedas;Gelieve munten;Veuillez insérer des;Gelieve munten;Lütfen bozuk para girin;Proszę włożyć monety;Vă rugăm să;Bitte Münzen einwerfen;Veuillez insérer des;Si prega di;Vložte prosím mince;Vložte mince;Molimo umetnite kovanice;
;S16F271;-;-;pièces;inserire monete;-;monedas;-;in te voeren;pièces;in te voeren;-;-;introduceți monede;-;pièces;inserire monete;-;-;-;
;S18F262;PIN Verwaltung;PIN Administration;Administration PIN;Amministrazione PIN;PIN Verwaltung;Administración de PIN;Gestão de PIN;PIN beheer;Administration PIN;PIN beheer;PIN yönetimi;Zarządzanie PIN;Management PIN;PIN Verwaltung;Administration PIN;Amministrazione PIN;Správa PIN;Správa PIN;PIN izbornik;
;S18F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarc.;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S21F204;Neue PIN Liste;New PIN list;Liste des codes PIN;Nuovo elenco PIN;Neue PIN Liste;Nueva lista de PIN;Nova lista PIN;Nieuwe PIN lijst;Liste des codes PIN;Nieuwe PIN lijst;Yeni PIN listesi;Nowa lista PIN;Listă PIN nouă;Neue PIN Liste;Liste des codes PIN;Nuovo elenco PIN;Nový seznam PIN;Nový zoznam PIN;Nova PIN lista;
;S21F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S21F270;Neue PIN Liste erzeugen.;Create new PIN list.;Créer une nouvelle liste de codes PIN;Crea nuovo elenco PIN.;Neue PIN Liste erzeugen.;Crear nueva lista de PIN.;Gerar nova lista PIN;Maak een nieuwe PIN lijst.;Créer une nouvelle liste de codes PIN;Maak een nieuwe PIN lijst.;Yeni bir PIN listesi oluşturun.;Wygeneruj nową listę PIN.;Generați o nouă listă PIN.;Neue PIN Liste erzeugen.;Créer une nouvelle liste de codes PIN;Crea nuovo elenco PIN.;Vygenerujte nový seznam PIN.;Vytvorte nový zoznam PIN.;Kreiraj novu listu PIN-ova;
;S21F217;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;OK;
;S21F201;Achtung, alle PINs werden geändert. Sind Sie sicher?;Attention, all PINs will be changed. Are you sure?;Attention, toutes les PINs sont changeé. Êtes-vous sûre?;Attenzione, tutti i PIN verranno modificati. Sei sicuro?;Achtung, alle PINs werden geändert. Sind Sie sicher?;Atención, todos los PIN serán cambiados. ¿Está seguro?;Atenção, todos os PINs serão alterados. Tem certeza?;Let op, alle pincodes worden gewijzigd. Weet je het zeker?;Attention, toutes les PINs sont changeé. Êtes-vous sûre?;Let op, alle pincodes worden gewijzigd. Weet je het zeker?;Dikkat, tüm PIN'ler değiştirilecektir. Emin misin?;Uwaga, wszystkie PIN zostaną zmienione. Jesteś pewny?;Atenție, toate codurile PIN vor fi modificate. Esti sigur?;Achtung, alle PINs werden geändert. Sind Sie sicher?;Attention, toutes les PINs sont changeé. Êtes-vous sûre?;Attenzione, tutti i PIN verranno modificati. Sei sicuro?;Pozor, všechny PINy budou změněny. Jsi si jistá?;Pozor, všetky PINy budú zmenené. Si si istý?;Pažnja, svi PIN-ovi će biti promjenjeni. Da li želite nastaviti?;
;S21F205;Abbrechen;Abort;Avorter;Aborto;Abbrechen;Abortar;Abortar;Afbreken;Avorter;Afbreken;Iptal;Anulować;Avorta;Abbrechen;Avorter;Aborto;Přerušit;Prerušiť;Prekid;
;S22F203;Mindestbetrag;Minimum amount;Montant minimal;Quantità minima;Mindestbetrag;Monto minimo;Valor mínimo;Minimale hoeveelheid;Montant minimal;Minimale hoeveelheid;Minimum miktar;Stawka minimalna;Cantitatea minima;Mindestbetrag;Montant minimal;Quantità minima;Minimální množství;Minimálna suma;Minimalan iznos;
;S22F208;Preis pro Stunde;Price per hour;Prix par heure;Prezzo all'ora;Preis pro Stunde;Precio por hora;Preço por hora;Prijs per uur;Prix par heure;Prijs per uur;Saatlik fiyat;Cena za godzinę;Pret pe ora;Preis pro Stunde;Prix par heure;Prezzo all'ora;Cena za hodinu;Cena za hodinu;Cijena po satu;
;S22F201;Mehrwertsteuer %;VAT %;T.V.A. %;IVA %;Mehrwertsteuer %;IVA %;Imposto Valor Acr. %;BTW %;T.V.A. %;BTW %;Katma değer Vergisi %;Podatek obrotowy %;Taxa pe valoare adaugata %;Mehrwertsteuer %;T.V.A. %;IVA %;Daň z přidané hodnoty %;Daň z pridanej hodnoty %;PDV %;
;S22F204;Tarif 2;Tariff 2;Tarif 2;Tariffa 2;Tarif 2;Tarifa 2;Tarifa 2;Tarief 2;Tarif 2;Tarief 2;Tarife 2;Taryfa 2;Tariful 2;Tarif 2;Tarif 2;Tariffa 2;Tarif 2;Tarifa 2;Tarifa 2;
;S22F207;Höchstparkdauer;Maximum parking;Maximale de;Parcheggio;Höchstparkdauer;Estacionamiento;Tempo máx. estacion.;Maximaal parkeren;Maximale de;Maximaal parkeren;Maksimum park süresi;Maksymalny czas;Timp maxim de;Höchstparkdauer;Maximale de;Parcheggio;Maximální doba parkování;Maximálny čas parkovania;Maksimalno trajanje;
;S22F206;-;-;stationnement;massimo;-;máximo;-;-;stationnement;-;-;parkowania;parcare;-;stationnement;massimo;-;-;parkinga;
;S22F220;Münzen;Coins;Monnaie;Monete;Münzen;Monedas;Moedas;Munten;Monnaie;Munten;Madeni paralar;Monety;Monede;Münzen;Monnaie;Monete;Mince;Mince;Kovanice;
;S22F221;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QRCode;QR kod;
;S22F222;Mü+QR;Co+QR;Mo+QR;Mo+QR;Mü+QR;Mo+QR;Mo+QR;Mu+QR;Mo+QR;Mu+QR;Ma+QR;Mo+QR;Mo+QR;Mü+QR;Mo+QR;Mo+QR;Mi+QR;Mi+QR;Ko+QR;
;S22F211;Tagesticket;Day ticket;Billet journalier;Biglietto giornaliero;Tagesticket;Boleto de un día;Bilhete diário;Dagkaart;Billet journalier;Dagkaart;Günlük bilet;Bilet dzienny;Bilet de zi;Tagesticket;Billet journalier;Biglietto giornaliero;Denní jízdenka;Denný lístok;Dnevna karta;
;S22F215;Stunden;Hours;Heures;Ore;Stunden;Horas;Horas;Uren;Heures;Uren;Saat;Godziny;Ore;Stunden;Heures;Ore;Hodin;Hodiny;Sati;
;S22F216;Minuten;Minutes;Minutes;Minuti;Minuten;Minutos;Minutos;Minuten;Minutes;Minuten;Dakika;Minuty;Minute;Minuten;Minutes;Minuti;Minut;Minút;Minute;
;S22F217;Nein;No;Non;No;Nein;No;Não;Nee;Non;Nee;Hayır;Nie;Nu;Nein;Non;No;Ne;Nie;Ne;
;S22F218;Ja;Yes;Oui;Sì;Ja;Sí;Sim;Ja;Oui;Ja;Evet;Tak;Da;Ja;Oui;Sì;Ano;Áno;Da;
;S22F233;Zahlungsweise;Payment mode;Mode de paier;Metodo di pagare;Zahlungsweise;Modo de pago;Forma de pagar;Betaalmethode;Mode de paier;Betaalmethode;Ödeme şekli;Metoda płatności;Metodă de plată;Zahlungsweise;Mode de paier;Metodo di pagare;Způsob platby;Spôsob platby;Način plaćanja;
;S22F240;Tabelle;Table;Tableau;Tavolo;Tabelle;Tabla;Mesa;Tabel;Tableau;Tabel;Tabel;Tabela;Tabel;Tabelle;Tableau;Tavolo;Tabulka;Tabuľka;Tabela;
;S22F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S23F220;Werkseinstellung bei Neustart;Factory setting on restart;Réglage d'usine au redémarrage;Impostazione di fabbrica al riavvio;Werkseinstellung bei Neustart;Configuración de fábrica al reiniciar;Config. de fábrica ao reiniciar;Fabrieksinstelling bij herstart;Réglage d'usine au redémarrage;Fabrieksinstelling bij herstart;Yeniden başlatmada fabrika ayarı;Ustawienie fabryczne przy restarcie;Resetarea din fabrică la repornire;Werkseinstellung bei Neustart;Réglage d'usine au redémarrage;Impostazione di fabbrica al riavvio;Tovární nastavení při restartu;Výrobné nastavenie pri reštarte;Vrati na tvorničke postavke prilikom ponovnog pokretanja;
;S23F203;Serialnumber;Serialnumber;Serialnumber;Numero di serie;Serialnumber;Número de serie;Número de série;Serienummer;Serialnumber;Serienummer;Seri numarası;Numer seryjny;Număr de serie;Serialnumber;Serialnumber;Numero di serie;Sériové číslo;Sériové číslo;Serijski broj;
;S23F202;Version;Version;Version;Versione;Version;Versión;Versão;Versie;Version;Versie;Versiyon;Wersja;Versiune;Version;Version;Versione;Verze;Verzia;Verzija;
;S23F204;Gerät;Device;Appareil;Dispositivo;Gerät;Dispositivo;Dispositivo;Apparaat;Appareil;Apparaat;Cihaz;Urządzenie;Dispozitiv;Gerät;Appareil;Dispositivo;Přístroj;Zariadenie;Uređaj;
;S23F205;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;Firmware;
;S23F206;Screen;Screen;Screen;Schermo;Screen;Pantalla;Tela;Scherm;Screen;Scherm;Ekran;Ekran;Ecran;Screen;Screen;Schermo;Obrazovka;Obrazovke;Zaslon;
;S23F207;Speicher bereinigen;Clean up memory;Nettoyer la mémoire;Pulisci la memoria;Speicher bereinigen;Limpiar la memoria;Limpar a memória;Geheugen opschonen;Nettoyer la mémoire;Geheugen opschonen;Belleği temizle;Wyczyść pamięć;Curățați memoria;Speicher bereinigen;Nettoyer la mémoire;Pulisci la memoria;Vyčistěte paměť;Vyčistite pamäť;Izbrisati memoriju;
;S23F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S23F201;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Patch;Parče;
;S23F217;Nein;No;Non;No;Nein;No;Não;Nee;Non;Nee;Hayır;Nie;Nu;Nein;Non;No;Ne;Nie;Ne;
;S23F218;Ja;Yes;Oui;Sì;Ja;Sí;Sim;Ja;Oui;Ja;Evet;Tak;Da;Ja;Oui;Sì;Ano;Áno;Da;
;S26F270;Bitte SD Karte einlegen;Please insert SD card;Veuillez insérer la carte SD;Inserire la scheda SD;Bitte SD Karte einlegen;Inserte la tarjeta SD;Por favor insira cart. SD;Plaats a.u.b. SD-kaart;Veuillez insérer la carte SD;Plaats a.u.b. SD-kaart;Lütfen SD kartı takın;Proszę włożyć kartę SD;Vă rugăm să introduceți cardul SD;Bitte SD Karte einlegen;Veuillez insérer la carte SD;Inserire la scheda SD;Vložte prosím SD kartu;Vložte SD kartu;Umetnite SD karticu;
;PARKSCHEIN;Parkschein;Parkticket;Ticket de parking;Biglietto del parco;Parkschein;Boleto estacionamiento;Bilhete estacion.;Parkeerbon;Ticket de parking;Parkeerbon;Otopark bileti;Bilet parkingowy;Bilet de parcare;Parkschein;Ticket de parking;Biglietto del parco;Parkovací lístek;Parkovací lístok;Parkirna  karta;
;DATUM;%d.%m.%y;%y/%m/%d;%d.%m.%y;%d.%m.%y;%d.%m.%y;%y/%m/%d;%d.%m.%y;%y/%m/%d;%d.%m.%y;%y/%m/%d;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;%d.%m.%y;
;ZEIT;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;%H:%M;
;ZEITS;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;%H:%M:%S;
;LCODE;DE;EN;FR;IT;DE;ES;PT;NL;FR;FL;TR;PL;RO;DE;FR;IT;CS;SK;HR;
;UST;UST;VAT;TVA;IVA;UST;IVA;IVA;BTW;TVA;BTW;VAT;VAT;VAT;UST;TVA;IVA;VAT;VAT;PDV;
;USTK;Umsatzst.;VAT;TVA;IVA;Umsatzst.;IVA;Imp. vend.;BTW;TVA;BTW;KDV;PTU;Taxa de vanzari;Umsatzst.;TVA;IVA;Prodejní daň;DPH;PDV;
;MINDESTENS;mind.;at least;au moins;almeno;mind.;al menos;no mín.;tenminste;au moins;tenminste;en azından;przynaj.;macar;mind.;au moins;almeno;alespoň;najmenej;najmanje;
;TAGESTICKET;Tagesticket ab;Dayticket from;Billiet jour de;Biglietto giornaliero da;Tagesticket ab;Billete de un día desde;Bilhete diário;Dagkaart vanaf;Billiet jour de;Dagkaart vanaf;Günlük bilet;Bilet dzienny od;Bilet de zi;Tagesticket ab;Billiet jour de;Biglietto giornaliero da;Denní jízdenka;Denný lístok;Dnevna karta od;
;TAG;Tag;day;jour;giorno;Tag;día;Dia;Dag;jour;Dag;Gün;Dzień;Zi;Tag;jour;giorno;Den;Deň;dan;
;TAGE;Tage;days;jours;giorni;Tage;días;Dias;Dagen;jours;Dagen;Günler;Dni;Zile;Tage;jours;giorni;Dní;Dni;dani;
;S27F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S27F5;QR Code scannen, TAN bekommen, eingeben;Scan QR code, get TAN, enter;Scan QR code, obtenir TAN, taper;Scansiona il codice QR, ottieni TAN, entra;QR Code scannen, TAN bekommen, eingeben;Escanee QR code, obtenga TAN, ingrese;Digitalize cód. QR, obtenha TAN, entre;Scan QR-code, ontvang TAN, voer in;Scan QR code, obtenir TAN, taper;Scan QR-code, ontvang TAN, voer in;QR kodunu tarayın, TAN alın, girin;Zeskanuj QR Code, uzyskaj TAN, wprowadź;Scanați QR Code, obțineți TAN, introduceți;QR Code scannen, TAN bekommen, eingeben;Scan QR code, obtenir TAN, taper;Scansiona il codice QR, ottieni TAN, entra;Naskenujte QR kód, získejte TAN, zadejte;Naskenujte QR kód, získajte TAN, zadajte;Skeniraj QR kod, uzmi TAN broj,pritisni enter=potvrda;
;S27F3;Ungültig;Invalid;Invalide;Non valido;Ungültig;Inválido;Inválido;Ongeldig;Invalide;Ongeldig;Geçersiz;Nieważny;Invalid;Ungültig;Invalide;Non valido;Neplatný;Neplatné;Nije valjano;
;S28F204;Tariftabelle;Tariff Table;Tableau tarifaire;Tabella tariffaria;Tariftabelle;Tabla de tarifas;Tabela tarifária;Tarieftabel;Tableau tarifaire;Tarieftabel;Tarife tablosu;Tabela taryfowa;Tabel tarifar;Tariftabelle;Tableau tarifaire;Tabella tariffaria;Tariff Table;Tariff Table;Tarifna tablica;
;S29F204;Tariftabelle;Tariff Table;Tableau tarifaire;Tabella tariffaria;Tariftabelle;Tabla de tarifas;Tabela tarifária;Tarieftabel;Tableau tarifaire;Tarieftabel;Tarife tablosu;Tabela taryfowa;Tabel tarifar;Tariftabelle;Tableau tarifaire;Tabella tariffaria;Tariff Table;Tariff Table;Tarifna tablica;
;S28F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S29F299;Zurück;Back;Retour;Ritorno;Zurück;Volver;Retornar;Rug;Retour;Rug;Dönüş;Zwrócić;Întoarcere;Zurück;Retour;Ritorno;Vrátit se;Návrat;Natrag;
;S28F215;Stunden;Hours;Heures;Ore;Stunden;Horas;Horas;Uren;Heures;Uren;Saat;Godziny;Ore;Stunden;Heures;Ore;Hodin;Hodiny;Sati;
;S28F216;Preis;Price;Prix;Prezzo;Preis;Precio;Preço;Prijs;Prix;Prijs;Fiyat;Cena;Preț;Preis;Prix;Prezzo;Cena;Cena;Cijena;
;S28F217;Stunden;Hours;Heures;Ore;Stunden;Horas;Horas;Uren;Heures;Uren;Saat;Godziny;Ore;Stunden;Heures;Ore;Hodin;Hodiny;Sati;
;S28F218;Preis;Price;Prix;Prezzo;Preis;Precio;Preço;Prijs;Prix;Prijs;Fiyat;Cena;Preț;Preis;Prix;Prezzo;Cena;Cena;Cijena;
;S29F215;Stunden;Hours;Heures;Ore;Stunden;Horas;Horas;Uren;Heures;Uren;Saat;Godziny;Ore;Stunden;Heures;Ore;Hodin;Hodiny;Sati;
;S29F216;Preis;Price;Prix;Prezzo;Preis;Precio;Preço;Prijs;Prix;Prijs;Fiyat;Cena;Preț;Preis;Prix;Prezzo;Cena;Cena;Cijena;
;S29F217;Stunden;Hours;Heures;Ore;Stunden;Horas;Horas;Uren;Heures;Uren;Saat;Godziny;Ore;Stunden;Heures;Ore;Hodin;Hodiny;Sati;
;S29F218;Preis;Price;Prix;Prezzo;Preis;Precio;Preço;Prijs;Prix;Prijs;Fiyat;Cena;Preț;Preis;Prix;Prezzo;Cena;Cena;Cijena;
;PROSTD;pro Stunde;per hour;par heure;all'ora;pro Stunde;por hora;por hora;per uur;par heure;per uur;saat başı;na godzinę;pe ora;pro Stunde;par heure;all'ora;za hodinu;za hodinu;po satu;
;DAVON;davon;from that;de la;da quello;davon;a partir de ese;a partir desse;van dat;de la;van dat;Bundan;z tego;din aceasta;davon;de la;da quello;od toho;odtiaľto;iz toga;
;PARKSCHEINVOM;Parkschein vom;Parkticket from;Ticket de parking de;Biglietto da;Parkschein vom;Boleto estacionamiento;Bilhete estacion. de;Parkeerbon vanaf;Ticket de parking de;Parkeerbon vanaf;Park bileti tarihi;Bilet parkingowy od;Bilet de parcare de la;Parkschein vom;Ticket de parking de;Biglietto da;Parkovací lístek od;Parkovací lístok od;Parkirna karta iz;
;GUTSCHEINVOM;Gutschein vom;Voucher from;Bon d achat de;Buono da;Gutschein vom;Vale de;Voucher de;Waardebon van;Bon d achat de;Waardebon van;Kupon tarihi;Kupon od;Voucher de la;Gutschein vom;Bon d achat de;Buono da;Poukaz od;Poukaz od;Vaučer iz;
;PARKDAUER;Parkdauer;Parking duration;Duree de parking;Durata del parcheggio;Parkdauer;Duración del estacionamiento;Duração do estacion.;Parkeerduur;Duree de parking;Parkeerduur;Park süresi;Czas parkowania;Durata de parcare;Parkdauer;Duree de parking;Durata del parcheggio;Doba parkování;Trvanie parkovania;Duljina parkiranja;
;PARKENBIS;Parken bis;Parking until;Parking d ici a;Parcheggio fino al;Parken bis;Estacionamiento hasta;Estacion. até;Parkeren tot;Parking d ici a;Parkeren tot;Kadar park etmek;Parkowanie do;Parcare până la;Parken bis;Parking d ici a;Parcheggio fino al;Parkování do;Parkovanie do;Parkiranje do;
;GUELTIGBIS;Gueltig bis;Valid until;Valable jusqu a;Valido fino a;Gueltig bis;Válido hasta;Data de Expiração;Geldig tot;Valable jusqu a;Geldig tot;Kadar geçerli;Ważny do;Data expirarii;Gueltig bis;Valable jusqu a;Valido fino a;Datum spotřeby;Dátum spotreby;Valjano do;
;STD;Std.;h;h;h;Std.;h;H.;h;h;h;Sa.;Godziny;Ore;Std.;h;h;h.;H.;sat;
;MIN;Min.;min;min;min;Min.;min;Min.;min;min;min;Dk.;Minuty;Minute;Min.;min;min;m.;Min.;min;
;BARZAHLUNG;Barzahlung;Cash payment;Payment comptant;Pagamento in contanti;Barzahlung;Pago en efectivo;Pagamento em dinheiro;Contante betaling;Payment comptant;Contante betaling;Nakit ödeme;Zapłata gotówką;Plată în numerar;Barzahlung;Payment comptant;Pagamento in contanti;Platba v hotovosti;Platba v hotovosti;Gotovinsko plaćanje;
;BARGELDLOS;bargeldlos;cashless;sans argent liqu;senza soldi;bargeldlos;sin efectivo;sem dinheiro;zonder contant geld;sans argent liqu;zonder contant geld;Nakitsiz;bezgotówkowy;fără numerar;bargeldlos;sans argent liqu;senza soldi;Bezhotovostně;bezhotovostne;bezgotovinsko;
;NOTVALID1;Nicht signierter Ausgabereport.;Informal report, not signed.;Informal report, not signed.;Informal report, not signed.;Nicht signierter Ausgabereport.;Informe informal, no firmado.;Relatório de saída não assinado.;Informeel rapport, niet;Informal report, not signed.;Informeel rapport, niet;İmzasız çıktı raporu.;Niepodpisany raport wyjściowy.;Raport de ieșire nesemnat.;Nicht signierter Ausgabereport.;Informal report, not signed.;Informal report, not signed.;Nepodepsaná výstupní sestava.;Nepodpísaný protokol.;Nepotpisano izvješće o izlazu.;
;NOTVALID2;Nur nachrichtliche Werte.;Only for information.;Only for information.;Only for information.;Nur nachrichtliche Werte.;Solo para información.;Valores apenas informativos.;ondertekend. Alleen ter informatie.;Only for information.;ondertekend. Alleen ter informatie.;Yalnızca bilgi değerleri.;Tylko wartości informacyjne.;Numai valori informative.;Nur nachrichtliche Werte.;Only for information.;Only for information.;Pouze informativní hodnoty.;Len informatívne hodnoty.;Samo informacijske vrijednosti.;
;NOTVALID3;Kann nicht als Umsatznachweis;Not usable as a valid;Not usable as a valid;Not usable as a valid;Kann nicht als Umsatznachweis;No se puede utilizar como una;Não pode ser usado como prova;informatie. Niet bruikbaar als;Not usable as a valid;informatie. Niet bruikbaar als;Vergi makamları için satış;Nie może służyć jako dowód;Nu poate fi folosit ca dovadă;Kann nicht als Umsatznachweis;Not usable as a valid;Not usable as a valid;Nelze použít jako doklad;Nemožno použiť ako doklad;Ne može se koristiti kao;
;NOTVALID4;fuer die Steuerbehoerden;revenue statement for;revenue statement for;revenue statement for;fuer die Steuerbehoerden;declaración de ingresos válida;de venda para as;geldig inkomstenoverzicht voor;revenue statement for;geldig inkomstenoverzicht voor;kanıtı olarak kullanılamaz.;sprzedaży dla;de vânzare pentru;fuer die Steuerbehoerden;revenue statement for;revenue statement for;o prodeji pro;o predaji pre;izvješće o prodaji za;
;NOTVALID5;verwendet werden.;tax authorities.;tax authorities.;tax authorities.;verwendet werden.;para las autoridades fiscales.;autoridades fiscais.;de Belastingdienst.;tax authorities.;de Belastingdienst.;-;organów podatkowych.;autoritățile fiscale.;verwendet werden.;tax authorities.;tax authorities.;finanční úřady.;daňové úrady.;porezne vlasti.;
;USTREPORT;USt;VAT;TVA;IVA;USt;IVA;IVA;BTW;TVA;BTW;KDV;PTU;TVA;USt;TVA;IVA;DPH;DPH;PDV;
;PROTOKOLLTITEL;Protokoll verkaufte Parkscheine;Protocol sold parking tickets;Protocole des tickets vendus;Protocollo biglietti venduti;Protokoll verkaufte Parkscheine;Protocolo de billetes de estacionamiento vendidos;Protoc. bilhetes estacion. vend.;Protocol verkochte parkeerbonnen;Protocole des tickets vendus;Protocol verkochte parkeerbonnen;Protokol satılan park biletleri.;Rejestr sprzedanych bilet. park.;Protocol vândut bilete d parcare;Protokoll verkaufte Parkscheine;Protocole des tickets vendus;Protocollo biglietti venduti;Protokol prodával parkovací lístky;Protokol preda. parkov. lístkov;Prodane minute perkirnih karata;
;PROTOKOLLNR;Protokoll-Nr.;Protocol no.;Protocole no;Protocollo no.;Protokoll-Nr.;Protocolo no.;Protocolo nº;Protocol nr.;Protocole no;Protocol nr.;Protokol num.;Protokół nr.;Protocolul nr.;Protokoll-Nr.;Protocole no;Protocollo no.;Protokol č.;Protokol č.;Protokol br.;
;SIGNATUR;Signatur;Signature;Signature;Segnatura;Signatur;Signature;Assinat.;Signatuur;Signature;Signatuur;Imza;Sygnatura;Semnătură;Signatur;Signature;Segnatura;Podpis;Podpis;Potpis;
;ERZEUGT;erzeugt;generated;généré;generato;erzeugt;generado;gerado;gegenereerd;généré;gegenereerd;oluşturulan;wygenerowany;generat;erzeugt;généré;generato;vygenerován;vygenerovaný;generirano;
;BITTETICKETSLOTA;Bitte Ticketslot bestellen;Please order ticketslot;Please order ticketslot;Please order ticketslot;Bitte Ticketslot bestellen;Please order ticketslot;Please order ticketslot;Please order ticketslot;Please order ticketslot;Please order ticketslot;Please order ticketslot;Please order ticketslot;Please order ticketslot;Bitte Ticketslot bestellen;Please order ticketslot;Please order ticketslot;Please order ticketslot;Please order ticketslot;Molimo naručite ticketslot;
;BITTETICKETSLOTB;durch Ueberweisung unter;by wiring under;by wiring under;by wiring under;durch Ueberweisung unter;by wiring under;by wiring under;by wiring under;by wiring under;by wiring under;by wiring under;by wiring under;by wiring under;durch Ueberweisung unter;by wiring under;by wiring under;by wiring under;by wiring under;bankovnim transferom;
;BITTETICKETSLOTC;Angabe einer oder mehrerer;specification of one or;specification of one or;specification of one or;Angabe einer oder mehrerer;specification of one or;specification of one or;specification of one or;specification of one or;specification of one or;specification of one or;specification of one or;specification of one or;Angabe einer oder mehrerer;specification of one or;specification of one or;specification of one or;specification of one or;navodeći jedan ili više brojeva;
;BITTETICKETSLOTD;Ordernummern;more order numbers;more order numbers;more order numbers;Ordernummern;more order numbers;more order numbers;more order numbers;more order numbers;more order numbers;more order numbers;more order numbers;more order numbers;Ordernummern;more order numbers;more order numbers;more order numbers;more order numbers;brojeva narudžbe;
;BANKVERBINDUNG;Bankverbindung siehe unter:;Bank account see under:;Bank account see under:;Bank account see under:;Bankverbindung siehe unter:;Bank account see under:;Bank account see under:;Bank account see under:;Bank account see under:;Bank account see under:;Bank account see under:;Bank account see under:;Bank account see under:;Bankverbindung siehe unter:;Bank account see under:;Bank account see under:;Bank account see under:;Bank account see under:;Bankovne podatke vidi pod:;
;SPEICHERSIGNIERUNG1;Speichersignierung anfordern;Request for storage signing;Request for storage signing;Request for storage signing;Speichersignierung anfordern;Request for storage signing;Request for storage signing;Request for storage signing;Request for storage signing;Request for storage signing;Request for storage signing;Request for storage signing;Request for storage signing;Speichersignierung anfordern;Request for storage signing;Request for storage signing;Request for storage signing;Request for storage signing;Zatražite memorijski potpis;
;SPEICHERSIGNIERUNG2;durch Angabe der Ordernummer;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;durch Angabe der Ordernummer;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;durch Angabe der Ordernummer;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;by specification of the ordernumber;navodeći broj naloga;
;SPEICHERSIGNIERUNG3;Details siehe unter:;Details see under:;Details see under:;Details see under:;Details siehe unter:;Details see under:;Details see under:;Details see under:;Details see under:;Details see under:;Details see under:;Details see under:;Details see under:;Details siehe unter:;Details see under:;Details see under:;Details see under:;Details see under:;Pojedinosti vidi pod:;
;VON;von;by;de;da;von;de;de;van;de;van;ile;od;din;von;de;da;od;od;od;
;BATTERYA;Bitte Batteriestatus;Please check;Veuillez vérifier l'état de;Si prega di controllare lo stato;Bitte Batteriestatus;Por favor, compruebe el estado;Por favor, verifique o estado;Controleer de;Veuillez vérifier l'état de;Controleer de;Lütfen pil durumunu;Sprawdź stan;Vă rugăm să verificați;Bitte Batteriestatus;Veuillez vérifier l'état de;Si prega di controllare lo stato;Zkontrolujte prosím;Skontrolujte stav;Provjerite stanje;
;BATTERYB;ueberpruefen;battery state;la batterie;della batteria;ueberpruefen;de la batería;da bateria;batterijstatus;la batterie;batterijstatus;kontrol edin;baterii;starea bateriei;ueberpruefen;la batterie;della batteria;stav baterie;batérie;baterije;
;BATTERYC;Dann neu starten;Then restart;Redémarrez ensuite l'appareil;Dopo quel riavvio;Dann neu starten;Después de ese reinicio;Em seguida, reinicie;Daarna opnieuw opstarten;Redémarrez ensuite l'appareil;Daarna opnieuw opstarten;Bundan sonra yeniden başlatın;Po tym restarcie;După această repornire;Dann neu starten;Redémarrez ensuite l'appareil;Dopo quel riavvio;Po tomto restartu;Po tomto reštarte;Nakon ponovnog pokretanja;


]]

--************************************************************************
--private


if arg ~= nil and arg[1] == 'test7' then

    function Field.write(self) return nil end
    function start_timer() return nil end


    local x   = App:new(1)

    t(2,"DE")

    x.report_data = {}
    x:test1_generate_report(arg[2])

    
end


--************************************************************************
--private


if arg ~= nil and arg[1] == 'test75' then

    local x   = App:new(1)

    local x1  = base32_encode(arg[2])
    local x2  = base32_decode(x1)
    print(arg[2])
    print(x1)
    print(x2)
    print(arg[2])
    
end


--************************************************************************
--private

if arg ~= nil and arg[1] == 'test701' then

    local x   = App:new(1)


--    local text1 = "3c9bcf7b223e894984cc7bd7cd8871752889284a7b7c9ebe330167fc57c72a1d083687f56b21826bddda46ee4021c6174b89155a3b9b19b0dbcc6dfdea193391bd4d3ea0af33ee5b7f86df3d5c45462f05aa2f92b554ac2891cf3f34e10a37654626ba0a6dad93c858c6354c8b1d5491962b63c080bb226222"
--    local text2 = base62.decode(text1)
--    print(text2)
--    local text3 = bin.stohex(text2)
--    print(text3)
--    print("qqq4",text1_crypt)
    local text33 = "3c9bcf7b223e894984cc7bd7cd8871752889284a7b7c9ebe330167fc57c72a1d083687f56b21826bddda46ee4021c6174b89155a3b9b19b0dbcc6dfdea193391bd4d3ea0af33ee5b7f86df3d5c45462f05aa2f92b554ac2891cf3f34e10a37654626ba0a6dad93c858c6354c8b1d5491962b63c080bb226222"
    local text3 = "6f4a57ada0dc62baf753dc34b4bc5f75288ab5477b7c9ebe33f119565742e9c8108e22c75f359162fd5b4a29cacf7ccadb1e430f3b5779ea0c9e2342f86d5d28b87135ed1f3de8ee0e1267751dbf20f83f3fea463c9a602b962d113a4e1231fe717303740e83094c5907646da7e64c95c330b0d522ba797733a6887ce0027bbe476a0dd25834"
    local text1_bin       = x:decrypt(pub_key, text3 )
    print(text1_bin)
--    local text11           = bin.stohex( text1_bin )
--    print("qqq7",text11)



end

--************************************************************************
--private


if arg ~= nil and arg[1] == 'test702' then

    local x   = App:new(1)


--    local text1 = "3c9bcf7b223e894984cc7bd7cd8871752889284a7b7c9ebe330167fc57c72a1d083687f56b21826bddda46ee4021c6174b89155a3b9b19b0dbcc6dfdea193391bd4d3ea0af33ee5b7f86df3d5c45462f05aa2f92b554ac2891cf3f34e10a37654626ba0a6dad93c858c6354c8b1d5491962b63c080bb226222"
--    local text2 = base62.decode(text1)
--    print(text2)
--    local text3 = bin.stohex(text2)
--    print(text3)
--    print("qqq4",text1_crypt)
    local text33 = "3c9bcf7b223e894984cc7bd7cd8871752889284a7b7c9ebe330167fc57c72a1d083687f56b21826bddda46ee4021c6174b89155a3b9b19b0dbcc6dfdea193391bd4d3ea0af33ee5b7f86df3d5c45462f05aa2f92b554ac2891cf3f34e10a37654626ba0a6dad93c858c6354c8b1d5491962b63c080bb226222"
    local text3 = "6f4a57ada0dc62baf753dc34b4bc5f75288ab5477b7c9ebe33f119565742e9c8108e22c75f359162fd5b4a29cacf7ccadb1e430f3b5779ea0c9e2342f86d5d28b87135ed1f3de8ee0e1267751dbf20f83f3fea463c9a602b962d113a4e1231fe717303740e83094c5907646da7e64c95c330b0d522ba797733a6887ce0027bbe476a0dd25834"
    local text1_bin       = x:decrypt(pub_key, text3 )
    print(text1_bin)
--    local text11           = bin.stohex( text1_bin )
--    print("qqq7",text11)

end

--************************************************************************
--private


if arg ~= nil and arg[1] == 'test703' then

    local x   = App:new(1)
--    local x0  = "ffffffff444455556666777788884444ffffeeee44445555666677778888ffff"  print(x0)
    local x0 = "fedc1"  print(x0)
    local x1  = base27precise.encode(x0)   print(x1)
    local x2  = base27precise.decode(x1)   print(x2)

--    print(#x0)
--    print(#x1)
--    print(#x2)
    
end

--************************************************************************
--private


if arg ~= nil and arg[1] == 'test801' then

    local x   = App:new(1)
--    local x0  = "ffffffff444455556666777788884444ffffeeee44445555666677778888ffff"  print(x0)
    function stop_timer()  return nil end
    function start_timer() return nil end

    x:zeitumstellung()

--    print(#x0)
--    print(#x1)
--    print(#x2)
    
end

--************************************************************************

if arg ~= nil and arg[1] == 'fcheck' then

    a = fcheck(-99,tonumber,"00a")
    print(a)

end

bypass 1.0, Devloped By El Moujahidin (the source has been moved and devloped)
Email: contact@elmoujehidin.net bypass 1.0, Devloped By El Moujahidin (the source has been moved and devloped) Email: contact@elmoujehidin.net