diff --git a/README.md b/README.md index 29a0e8e..8a6eb34 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,92 @@ -# hwmf -Soon. +PARTY VERSION! + +Credits where credits are due: +============================== + +The following 3rd party resources were used: + +pmf_player - https://github.com/JarkkoPFC/arduino-music-player/ +exomizer - https://bitbucket.org/magli143/exomizer + +Colors for the screen border during the C64/Amiga-ish part were sampled from +"A1000" by J.O.E, see http://artcity.bitfellas.org/index.php?a=show&id=11210 + +3x5 by memesbruh03 - https://www.dafont.com/3x5-2.font +Perfect DOS VGA by Zeh Fernando - https://www.dafont.com/perfect-dos-vga-437.font +A500 Topaz font, TTF file of unknown origin + + +How to compile the thing +======================== + +The easiest way is to get Arduino IDE. +See https://www.arduino.cc/en/Main/Software + +Load democode.ino in the Arduino IDE, compile it, flash it, done. +Possibly read the comments in democode.ino in case the compiler is too old +or does not like the code and refuses to compile it. + +Settings: + +Board: "Arduino/Genuino Mega or Mega 2560" +Processor: ATmega2560 +(5V 16 MHz if that's a choice) + +Note that the code in the source archive is the party version; +it's horrible and hacky and fixed to work just before the deadline, +and it's likely i'll clean it up a bit later. + +See https://github.com/fgenesis/hwmf for up-to-date code. + + +Getting audio out +================== + +Audio is output to OCR0A (Pin #13) and OCR0B (Pin #4). +This demo has mono audio so either one works. +Stereo is a future possibility so in case you're building an add-on board +you might plan ahead for that. + +Connect it like this: + +Pin 4 o-----||-----> left audio channel +Pin 13 o-----||-----> right audio channel +Any GND pin directly to the audio GND. + +As insulating capacitors ||, 1.0 µF works fine. Something close to that probably too. +Don't connect the pins directly to the audio, they are at 0-5V level +(instead of approx. -1..+1V that analog audio usually has) and the receiving device +might be unhappy about that (magic smoke worst case). The caps make this a lot safer, +and it also sounds much better as they center the signal around GND. + +The on-board LED is also connected to Pin #13 so if you see it pulse a little after a +few seconds after reset, that's an indication it's playing audio correctly. + + +Changing the display controller +=============================== + +The binaries in this release archive are compiled for the ILI9481 display controller. +There is (untested) support for ILI9486 and HX8357C as well, so if you have those, +go to democode/src/demolib/cfg-demo.h, look for this: + +typedef fglcd::preset::ILI9481_Mega_Shield LCD; + +and change it to + +typedef fglcd::preset::ILI9486_Mega_Shield LCD; +-or- +typedef fglcd::preset::HX8357C_Mega_Shield LCD; + +In case your display controller is 480x320 but none of the above, +feel free to contact me and I might add support for your controller. +Alternatively create a new type describing the controller and add the init code +required to start up the display. In case you do this, please send me a pull request +so the display library can support more controllers! + +Those MIPI displays are pretty compatible so it should work just fine after +it's set up properly with the correct initcode. + + +-- fgenesis, +for Revision 2020 diff --git a/democode/cbm64.cpp b/democode/cbm64.cpp new file mode 100644 index 0000000..2de52af --- /dev/null +++ b/democode/cbm64.cpp @@ -0,0 +1,99 @@ +#include "cbm64.h" +#include "src/demolib/decomp.h" +#include "data.h" + +#ifdef __GNUC__ +#pragma GCC optimize ("Os") +#endif + +typedef LCD::PixelPos PixelPos; +typedef LCD::ColorType Color; +typedef LCD::DimType DimType; + +static void loadbuf(void *dst, fglcd::FarPtr src, unsigned pksz) +{ + decompressRAM(dst, src, pksz); +} + +void CBM64Font::loadCharset1() +{ + loadbuf(_data, fglcd_get_farptr(data_exo_cbm1), Countof(data_exo_cbm1)); +} + +void CBM64Font::loadCharset2() +{ + loadbuf(_data, fglcd_get_farptr(data_exo_cbm2), Countof(data_exo_cbm2)); +} + +void CBM64Font::drawchar(char c, const LCD::ColorType fg, const LCD::ColorType bg) const +{ + typename fglcd::RAM::Pointer fp = _data + uint16_t(uint8_t(c)) * 8u; + for(uint8_t yy = 0; yy < 8; ++yy) + { + uint8_t dat = fglcd::RAM::readIncFast(fp); + + FGLCD_REP_8({ + LCD::sendPixel(dat & 0x80 ? fg : bg); + dat <<= 1u; + }); + } +} + +LCD::PixelPos CBM64Font::drawbuf(const uint8_t * s, unsigned len, LCD::DimType x, LCD::DimType y, const LCD::ColorType fg, const LCD::ColorType bg) const +{ + const DimType startx = x; + DimType endy = y + 7; + while(len--) + { + const uint8_t c = fglcd::RAM::readIncFast(s); + if(UNLIKELY(c == '\n' || x >= LCD::WIDTH-8)) + { + y += 8; + endy += 8; + x = startx; + if(c == '\n') + continue; + } + + LCD::setxy_inl(x, y, x+7, endy); + drawchar(c, fg, bg); + x += 8; + } + return PixelPos(x,y); +} + + + +#if 0 + template + static NOINLINE void drawfont_1bit_solid_8x8_columnwise(typename FontMemory::Pointer const fontdata, typename StringMemory::Pointer s, DimType x, DimType y, const ColorType fg, const ColorType bg) + { + const DimType startx = x; + u8 c; + goto begin; + while((c = StringMemory::template read(s++))) + { + if(c != '\n' && (x+7) < WIDTH) + { + const unsigned fontOffs = c * 8; + for(u8 yy = 0; yy < 8; ++yy) + { + u8 dat = FontMemory::template read(&fontdata[fontOffs + yy]); + for(u8 xx = 0; xx < 8; ++xx, dat >>= 1) + { + sendPixel(dat & 0x1 ? fg : bg); + } + } + x += 8; + } + else + { + y += 8; + x = startx; + begin: + Chip::setxy_inl(y, x, y+7, XMAX); + } + } + return PixelPos(x,y); + } +#endif diff --git a/democode/cbm64.h b/democode/cbm64.h new file mode 100644 index 0000000..b9b9cf5 --- /dev/null +++ b/democode/cbm64.h @@ -0,0 +1,16 @@ +#pragma once + +#include "src/demolib/demo_def.h" + +class CBM64Font +{ +public: + static const unsigned size_unpacked = 0x800; + void loadCharset1(); + void loadCharset2(); + void drawchar(char c, const LCD::ColorType fg, const LCD::ColorType bg) const; + LCD::PixelPos drawbuf(const uint8_t *s, unsigned len, LCD::DimType x, LCD::DimType y, const LCD::ColorType fg, const LCD::ColorType bg) const; + +private: + uint8_t _data[size_unpacked]; +}; diff --git a/democode/data.def.h b/democode/data.def.h new file mode 100644 index 0000000..39a8b50 --- /dev/null +++ b/democode/data.def.h @@ -0,0 +1,12 @@ +#pragma once +#include "src/demolib/demo_def.h" +#include "src/demolib/packdef.h" +#include "src/demomath/fp8_8.h" +#include "src/demomath/fp16_16.h" + +#ifdef MCU_IS_PC +# define PGM_ALIGN(x) +#else +# define PGM_ALIGN(x) ALIGN(x) +#endif + diff --git a/democode/data.gen.cpp b/democode/data.gen.cpp new file mode 100644 index 0000000..6ffc351 --- /dev/null +++ b/democode/data.gen.cpp @@ -0,0 +1,3380 @@ +#include "data.gen.h" + +const uint8_t data_exo_a2560_path[120] PROGMEM_FAR = { +104, 96, 66, 8, 206, 0, 0, 0, 0, 74, 0, 2, 76, 236, 192, 1, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 56, 21, 35, 3, 204, 41, 133, 128, 181, 224, 10, 14, 7, 203, 10, 164, 15, 20, 195, 213, 72, 182, 10, 237, 11, 15, 97, 107, 9, 79, 162, 163, 13, 115, 70, 0, 54, 14, 28, 246, 76, 168, 4, 180, 28, 163, 9, 152, 165, 28, 26, 0, 48, 47, 11, 97, 202, 66, 197, 142, 0, 87, 52, 5, 42, 166, 10, 59, 97, 13, 69, 106, 186, 28, 204, 64, 125, 131, 13, 131, 14, 42, 190, 148, 9, 6, 160, 224, 0, 42, 0, 0, 128 +}; +const uint8_t data_exo_a2560_title[131] PROGMEM_FAR = { +68, 134, 64, 32, 140, 224, 0, 0, 4, 162, 0, 106, 8, 194, 202, 18, 0, 0, 0, 0, 0, 70, 128, 0, 0, 0, 32, 28, 11, 106, 1, 200, 75, 14, 1, 105, 233, 193, 223, 78, 145, 119, 206, 205, 95, 13, 75, 78, 3, 29, 26, 160, 248, 98, 88, 116, 53, 95, 160, 212, 26, 2, 210, 77, 193, 78, 95, 44, 46, 134, 45, 111, 127, 205, 160, 65, 239, 119, 96, 31, 86, 70, 85, 230, 98, 69, 8, 134, 71, 137, 206, 161, 116, 44, 30, 228, 128, 228, 158, 116, 23, 67, 81, 7, 133, 8, 105, 34, 60, 49, 193, 17, 212, 56, 239, 176, 112, 184, 111, 2, 168, 20, 111, 128, 80, 8, 0, 5, 0, 0, 16 +}; +const uint8_t data_exo_cbm1[961] PROGMEM_FAR = { +34, 2, 2, 32, 32, 0, 0, 0, 2, 70, 6, 104, 138, 102, 172, 101, 4, 38, 2, 4, 12, 98, 12, 66, 4, 2, 60, 209, 102, 110, 96, 171, 98, 0, 24, 102, 105, 126, 24, 47, 124, 99, 24, 215, 113, 96, 96, 187, 140, 120, 108, 150, 166, 82, 246, 126, 121, 59, 82, 131, 48, 131, 111, 110, 74, 77, 216, 195, 14, 210, 24, 96, 211, 30, 12, 32, 215, 108, 56, 40, 57, 112, 69, 175, 96, 32, 56, 127, 99, 119, 127, 107, 76, 193, 198, 118, 126, 130, 136, 40, 243, 5, 13, 241, 151, 137, 248, 14, 161, 67, 156, 253, 44, 6, 155, 226, 126, 24, 7, 92, 95, 194, 45, 161, 74, 250, 107, 127, 119, 151, 51, 165, 22, 184, 173, 96, 253, 6, 170, 12, 48, 252, 235, 167, 48, 117, 245, 12, 18, 86, 124, 218, 98, 252, 76, 12, 26, 33, 240, 51, 37, 250, 16, 48, 127, 18, 167, 88, 16, 21, 104, 26, 226, 140, 178, 255, 18, 28, 75, 62, 47, 124, 113, 98, 148, 102, 221, 53, 70, 249, 243, 56, 175, 103, 63, 92, 171, 11, 185, 45, 96, 153, 75, 145, 8, 35, 141, 159, 23, 255, 37, 140, 192, 171, 131, 60, 54, 15, 119, 1, 92, 113, 3, 29, 96, 165, 249, 110, 118, 247, 109, 56, 72, 80, 68, 142, 27, 226, 98, 28, 144, 151, 171, 14, 30, 85, 127, 58, 11, 202, 159, 124, 141, 28, 51, 36, 254, 208, 179, 228, 4, 136, 49, 153, 137, 62, 55, 59, 134, 48, 204, 59, 202, 14, 162, 69, 148, 120, 225, 200, 95, 112, 139, 16, 161, 174, 176, 79, 20, 255, 17, 62, 8, 28, 62, 127, 134, 51, 88, 96, 10, 240, 2, 88, 38, 225, 48, 132, 12, 48, 37, 217, 224, 240, 238, 72, 236, 28, 15, 7, 225, 239, 249, 240, 224, 24, 136, 192, 60, 87, 224, 112, 56, 244, 28, 14, 7, 3, 37, 78, 183, 56, 112, 224, 37, 32, 177, 108, 96, 246, 217, 126, 2, 112, 42, 27, 54, 127, 14, 62, 28, 8, 188, 96, 48, 37, 189, 7, 15, 28, 213, 220, 195, 231, 152, 132, 169, 214, 39, 250, 102, 9, 253, 173, 149, 140, 94, 6, 79, 5, 187, 140, 59, 132, 60, 34, 94, 63, 25, 0, 125, 253, 3, 62, 118, 54, 15, 59, 127, 63, 31, 254, 3, 1, 15, 56, 240, 96, 56, 227, 5, 0, 64, 245, 209, 10, 204, 40, 51, 50, 33, 3, 129, 180, 52, 255, 255, 254, 252, 248, 240, 224, 192, 128, 5, 121, 90, 160, 31, 111, 176, 144, 15, 10, 5, 106, 248, 131, 94, 32, 53, 133, 220, 117, 240, 86, 4, 130, 238, 14, 30, 224, 24, 67, 7, 0, 55, 193, 50, 4, 116, 44, 107, 116, 30, 103, 139, 51, 131, 47, 13, 88, 13, 159, 195, 153, 145, 69, 159, 50, 182, 255, 231, 102, 129, 179, 5, 236, 131, 99, 26, 238, 40, 159, 53, 249, 135, 147, 45, 76, 165, 236, 129, 242, 118, 165, 6, 97, 6, 222, 145, 148, 155, 177, 134, 29, 164, 231, 193, 166, 225, 243, 65, 147, 174, 199, 80, 114, 143, 139, 94, 159, 64, 112, 254, 156, 136, 128, 148, 153, 131, 141, 137, 129, 5, 16, 81, 230, 10, 27, 227, 47, 19, 241, 241, 66, 135, 57, 250, 89, 249, 55, 196, 129, 231, 14, 184, 191, 132, 91, 66, 149, 245, 148, 128, 136, 46, 103, 74, 45, 113, 90, 193, 251, 249, 243, 85, 207, 249, 215, 78, 96, 235, 234, 243, 237, 173, 131, 157, 180, 3, 152, 243, 52, 67, 224, 102, 75, 244, 239, 207, 128, 37, 78, 176, 32, 42, 208, 53, 197, 25, 100, 0, 36, 56, 150, 193, 94, 131, 227, 157, 153, 41, 186, 107, 185, 243, 231, 199, 152, 94, 192, 185, 86, 23, 114, 90, 193, 50, 151, 34, 16, 71, 27, 62, 46, 0, 75, 25, 129, 87, 6, 120, 108, 30, 238, 2, 184, 226, 252, 58, 193, 75, 243, 145, 137, 238, 218, 199, 144, 160, 137, 28, 55, 196, 197, 227, 33, 47, 86, 241, 225, 170, 128, 116, 23, 149, 159, 131, 26, 56, 102, 73, 253, 161, 103, 200, 9, 16, 99, 51, 18, 193, 110, 119, 12, 97, 152, 119, 149, 241, 68, 139, 40, 241, 195, 144, 191, 143, 22, 33, 67, 93, 96, 158, 7, 223, 95, 247, 227, 193, 128, 67, 25, 172, 48, 5, 120, 1, 44, 19, 112, 207, 194, 243, 24, 18, 236, 31, 15, 247, 36, 118, 227, 240, 248, 112, 247, 252, 15, 31, 140, 68, 63, 30, 43, 31, 143, 250, 199, 227, 241, 248, 252, 18, 167, 91, 199, 143, 146, 31, 144, 88, 182, 48, 123, 108, 129, 129, 56, 21, 13, 201, 128, 135, 193, 227, 247, 94, 159, 24, 18, 222, 248, 240, 227, 234, 238, 60, 24, 76, 66, 84, 235, 19, 253, 153, 4, 254, 214, 202, 198, 47, 3, 79, 2, 221, 198, 29, 194, 30, 17, 47, 31, 140, 128, 62, 254, 252, 193, 137, 201, 135, 157, 128, 192, 224, 255, 252, 254, 7, 156, 15, 48, 28, 113, 130, 128, 32, 122, 232, 129, 254, 37, 100, 67, 252, 3, 104, 105, 0, 254, 1, 3, 7, 15, 31, 63, 127, 10, 242, 181, 224, 64, 223, 97, 240, 32, 20, 10, 213, 7, 6, 188, 64, 107, 11, 184, 235, 224, 172, 9, 5, 220, 28, 60, 31, 48, 134, 248, 0, 111, 130, 100, 8, 232, 88, 214, 232, 60, 207, 22, 103, 6, 94, 26, 176, 27, 48, 0, 4 +}; +const uint8_t data_exo_cbm2[942] PROGMEM_FAR = { +34, 2, 0, 32, 0, 0, 0, 0, 4, 102, 38, 138, 42, 198, 28, 99, 4, 98, 2, 4, 8, 8, 106, 228, 2, 32, 60, 209, 102, 110, 96, 174, 98, 0, 193, 78, 6, 62, 102, 147, 35, 96, 70, 124, 102, 132, 204, 211, 40, 130, 81, 6, 36, 184, 51, 75, 153, 126, 174, 14, 24, 62, 147, 2, 52, 105, 186, 6, 23, 2, 26, 54, 184, 56, 55, 3, 16, 129, 211, 141, 108, 120, 39, 6, 129, 80, 141, 102, 127, 25, 107, 99, 18, 54, 244, 12, 24, 56, 19, 73, 187, 48, 224, 131, 52, 204, 70, 90, 46, 60, 247, 165, 38, 164, 126, 194, 14, 111, 4, 5, 75, 27, 156, 232, 239, 99, 107, 127, 62, 153, 54, 166, 100, 142, 6, 191, 12, 120, 105, 126, 87, 24, 48, 81, 239, 20, 48, 123, 237, 12, 18, 116, 124, 182, 98, 252, 147, 12, 5, 148, 20, 56, 188, 253, 16, 48, 127, 9, 170, 208, 48, 19, 200, 98, 141, 84, 47, 233, 255, 8, 135, 3, 74, 184, 202, 98, 102, 74, 164, 70, 174, 37, 56, 169, 103, 63, 127, 179, 139, 65, 33, 130, 139, 144, 0, 200, 34, 199, 78, 220, 255, 217, 196, 239, 131, 43, 53, 40, 128, 126, 231, 110, 50, 3, 6, 140, 96, 161, 110, 158, 118, 54, 78, 60, 111, 115, 77, 28, 55, 196, 149, 28, 34, 80, 189, 14, 30, 117, 127, 80, 241, 235, 96, 124, 51, 78, 15, 22, 182, 209, 242, 225, 178, 209, 6, 41, 146, 147, 62, 1, 202, 196, 82, 28, 104, 105, 14, 76, 210, 81, 103, 70, 141, 31, 112, 125, 24, 168, 117, 140, 217, 228, 22, 154, 255, 13, 184, 182, 83, 211, 139, 153, 138, 115, 102, 5, 2, 210, 152, 40, 183, 137, 153, 86, 165, 134, 72, 239, 46, 110, 119, 152, 194, 232, 105, 24, 48, 105, 30, 60, 173, 220, 108, 56, 59, 169, 112, 246, 136, 96, 59, 236, 99, 119, 186, 48, 113, 118, 160, 126, 157, 35, 208, 175, 15, 65, 128, 153, 137, 14, 163, 116, 52, 46, 167, 145, 46, 24, 1, 232, 28, 248, 123, 61, 73, 118, 148, 119, 15, 169, 64, 80, 193, 252, 56, 157, 179, 213, 168, 255, 33, 232, 192, 160, 48, 198, 104, 16, 81, 51, 204, 64, 141, 153, 78, 102, 99, 3, 218, 240, 24, 14, 24, 87, 44, 2, 13, 161, 192, 128, 110, 228, 67, 3, 3, 90, 39, 169, 153, 204, 102, 96, 84, 21, 170, 31, 8, 115, 132, 15, 128, 77, 5, 10, 248, 5, 64, 28, 193, 22, 129, 175, 129, 62, 17, 161, 104, 6, 134, 134, 224, 16, 7, 192, 11, 104, 100, 193, 47, 1, 203, 3, 6, 32, 192, 96, 112, 26, 232, 163, 44, 48, 12, 240, 5, 103, 195, 153, 145, 69, 159, 69, 147, 169, 78, 249, 193, 153, 147, 35, 159, 70, 131, 153, 132, 204, 211, 40, 130, 81, 249, 36, 184, 51, 75, 153, 129, 174, 241, 231, 193, 147, 2, 52, 105, 186, 249, 23, 2, 26, 54, 184, 199, 55, 3, 16, 129, 211, 141, 147, 135, 39, 6, 129, 80, 141, 153, 128, 25, 148, 156, 18, 54, 244, 12, 24, 56, 19, 73, 187, 48, 224, 131, 52, 204, 70, 90, 46, 195, 247, 165, 38, 164, 129, 194, 241, 111, 4, 5, 75, 27, 156, 232, 239, 156, 148, 128, 193, 153, 201, 166, 100, 142, 6, 191, 243, 135, 105, 129, 87, 231, 207, 81, 239, 20, 48, 123, 237, 243, 237, 116, 131, 182, 157, 3, 147, 243, 5, 148, 20, 56, 188, 253, 239, 207, 128, 9, 170, 208, 48, 19, 200, 98, 141, 84, 47, 233, 0, 8, 135, 3, 74, 184, 202, 157, 153, 74, 164, 185, 174, 37, 199, 169, 152, 192, 127, 179, 139, 65, 33, 130, 139, 144, 255, 200, 34, 199, 78, 220, 0, 217, 196, 239, 131, 43, 53, 40, 128, 129, 231, 110, 50, 252, 249, 140, 159, 161, 145, 158, 137, 54, 78, 60, 111, 115, 77, 28, 55, 196, 149, 227, 34, 80, 189, 241, 225, 117, 128, 80, 241, 235, 159, 131, 51, 78, 15, 22, 182, 209, 242, 225, 178, 209, 6, 41, 146, 147, 193, 1, 202, 196, 82, 28, 104, 105, 241, 76, 210, 81, 103, 70, 141, 31, 143, 125, 24, 168, 117, 140, 217, 228, 116, 62, 62, 54, 226, 217, 79, 78, 46, 102, 41, 205, 152, 20, 11, 74, 96, 162, 222, 38, 101, 90, 150, 25, 35, 188, 185, 145, 222, 99, 11, 161, 164, 231, 193, 164, 225, 243, 173, 147, 199, 112, 238, 167, 143, 218, 159, 32, 239, 178, 156, 136, 232, 193, 198, 137, 129, 130, 116, 143, 66, 188, 61, 6, 2, 102, 241, 38, 141, 208, 208, 186, 158, 68, 46, 231, 7, 160, 115, 225, 236, 245, 37, 218, 136, 80, 62, 165, 1, 67, 7, 240, 226, 157, 207, 86, 160, 0, 135, 162, 63, 207, 131, 25, 160, 64, 15, 207, 190, 230, 252, 48, 61, 161, 15, 128, 31, 172, 249, 2, 13, 161, 63, 128, 110, 228, 67, 252, 3, 90, 39, 135, 236, 252, 96, 84, 21, 170, 224, 8, 115, 132, 240, 128, 77, 5, 10, 7, 5, 64, 28, 193, 22, 129, 175, 129, 62, 17, 161, 104, 6, 134, 134, 31, 16, 248, 192, 11, 104, 100, 193, 47, 254, 203, 252, 249, 32, 192, 159, 112, 26, 232, 163, 44, 48, 12, 240, 5, 96, 0, 2 +}; +const uint8_t data_exo_credits[120] PROGMEM_FAR = { +0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 2, 128, 104, 2, 34, 72, 2, 64, 4, 162, 38, 4, 67, 241, 111, 100, 101, 32, 133, 46, 10, 54, 102, 103, 167, 110, 115, 105, 79, 10, 71, 70, 222, 88, 44, 195, 51, 68, 159, 109, 52, 100, 103, 117, 234, 121, 10, 77, 73, 64, 99, 251, 46, 189, 116, 109, 123, 10, 83, 85, 112, 70, 111, 114, 193, 169, 110, 75, 32, 195, 170, 114, 115, 104, 69, 230, 79, 115, 112, 108, 245, 97, 121, 101, 114, 147, 98, 117, 74, 203, 40, 107, 175, 111, 189, 76, 109, 217, 112, 105, 39, 233, 128, 0, 0, 32 +}; +const uint8_t data_exo_greets[437] PROGMEM_FAR = { +0, 0, 0, 0, 0, 0, 0, 0, 64, 2, 0, 96, 238, 164, 206, 130, 68, 32, 38, 102, 106, 4, 168, 206, 68, 70, 45, 67, 32, 71, 208, 114, 101, 251, 116, 105, 110, 103, 115, 42, 210, 111, 42, 189, 10, 65, 98, 121, 166, 215, 182, 67, 245, 12, 101, 99, 178, 35, 21, 107, 46, 201, 88, 10, 218, 108, 190, 97, 165, 114, 122, 36, 211, 84, 87, 153, 66, 51, 45, 204, 121, 42, 117, 207, 171, 101, 149, 12, 137, 59, 146, 112, 218, 115, 125, 108, 124, 160, 92, 97, 122, 239, 37, 105, 126, 99, 121, 79, 82, 84, 73, 74, 68, 18, 75, 30, 101, 107, 238, 100, 156, 110, 220, 75, 85, 167, 66, 70, 108, 185, 165, 108, 103, 197, 104, 116, 88, 152, 98, 134, 85, 99, 108, 152, 194, 177, 38, 206, 101, 243, 17, 109, 63, 110, 107, 101, 56, 243, 116, 111, 222, 168, 179, 80, 232, 102, 32, 84, 33, 120, 50, 144, 236, 94, 112, 206, 72, 50, 107, 225, 51, 48, 20, 115, 106, 121, 98, 64, 142, 163, 182, 6, 90, 219, 70, 124, 114, 66, 200, 191, 179, 108, 103, 59, 222, 73, 110, 113, 117, 84, 205, 74, 215, 97, 72, 151, 42, 51, 76, 112, 201, 230, 235, 46, 56, 182, 102, 79, 73, 65, 33, 228, 78, 88, 115, 11, 105, 99, 76, 187, 20, 45, 89, 62, 188, 142, 118, 15, 171, 43, 72, 100, 238, 77, 141, 57, 58, 228, 115, 79, 45, 118, 137, 151, 159, 100, 217, 80, 108, 185, 108, 211, 154, 78, 220, 104, 45, 75, 179, 57, 226, 85, 76, 209, 233, 21, 243, 192, 204, 222, 79, 120, 121, 244, 108, 154, 80, 216, 45, 66, 62, 152, 85, 85, 60, 115, 109, 98, 101, 57, 153, 70, 86, 77, 158, 82, 97, 82, 217, 153, 57, 80, 86, 122, 102, 29, 32, 49, 57, 20, 137, 208, 66, 145, 83, 31, 24, 50, 108, 253, 247, 118, 54, 53, 214, 48, 50, 119, 99, 48, 81, 80, 230, 69, 38, 104, 69, 112, 67, 115, 103, 27, 148, 44, 89, 108, 179, 77, 167, 1, 85, 207, 194, 141, 100, 105, 2, 106, 28, 147, 99, 75, 81, 46, 32, 79, 110, 122, 148, 142, 38, 84, 210, 57, 65, 78, 20, 225, 98, 76, 59, 83, 73, 51, 86, 40, 142, 117, 65, 87, 48, 249, 115, 116, 104, 255, 197, 223, 88, 65, 89, 69, 53, 71, 231, 179, 166, 100, 215, 79, 85, 33, 30, 216, 0, 0, 2 +}; +const uint8_t data_exo_hwmf[581] PROGMEM_FAR = { +32, 0, 6, 32, 0, 0, 0, 0, 4, 40, 128, 166, 166, 166, 172, 195, 0, 74, 74, 106, 168, 196, 78, 26, 36, 132, 32, 33, 66, 17, 132, 160, 112, 65, 93, 28, 12, 0, 1, 94, 85, 67, 93, 139, 12, 114, 75, 2, 171, 137, 252, 73, 41, 93, 64, 115, 107, 68, 94, 162, 2, 124, 82, 74, 85, 66, 117, 87, 32, 39, 242, 63, 61, 80, 175, 150, 75, 113, 55, 74, 64, 96, 164, 8, 166, 236, 81, 109, 160, 172, 202, 67, 75, 152, 44, 215, 199, 254, 8, 1, 18, 4, 64, 167, 105, 8, 71, 87, 20, 25, 40, 182, 233, 154, 213, 94, 199, 74, 114, 203, 113, 17, 24, 132, 30, 229, 20, 246, 23, 163, 134, 5, 164, 10, 176, 35, 137, 70, 129, 89, 20, 224, 156, 12, 209, 3, 57, 96, 207, 111, 226, 11, 92, 240, 52, 44, 72, 58, 85, 53, 55, 21, 129, 213, 228, 87, 167, 121, 162, 114, 107, 49, 115, 190, 178, 75, 136, 200, 116, 220, 35, 89, 206, 26, 72, 106, 172, 245, 18, 251, 122, 42, 76, 16, 74, 76, 105, 227, 124, 162, 34, 29, 106, 113, 49, 133, 112, 50, 91, 111, 65, 32, 106, 33, 199, 101, 84, 0, 27, 135, 147, 148, 85, 71, 21, 142, 75, 72, 45, 23, 90, 11, 249, 115, 55, 215, 112, 5, 212, 90, 226, 175, 3, 96, 116, 210, 155, 225, 180, 80, 178, 79, 87, 142, 182, 30, 14, 197, 85, 60, 89, 75, 42, 194, 20, 108, 41, 162, 56, 191, 105, 78, 106, 233, 223, 239, 76, 82, 81, 62, 176, 115, 112, 159, 125, 74, 221, 36, 31, 251, 91, 114, 228, 54, 141, 26, 100, 68, 85, 6, 13, 85, 84, 28, 255, 105, 42, 233, 211, 122, 95, 65, 79, 235, 162, 61, 213, 98, 247, 9, 228, 93, 169, 188, 240, 150, 238, 64, 206, 169, 93, 244, 87, 110, 184, 78, 233, 238, 154, 119, 77, 109, 98, 70, 91, 113, 64, 90, 171, 239, 46, 17, 243, 182, 119, 104, 221, 160, 186, 217, 209, 198, 221, 109, 188, 169, 110, 194, 22, 86, 199, 113, 85, 83, 209, 91, 122, 209, 192, 45, 124, 192, 5, 43, 227, 89, 209, 59, 18, 176, 119, 199, 114, 191, 239, 79, 119, 20, 235, 36, 82, 55, 177, 106, 154, 195, 200, 42, 169, 105, 78, 58, 232, 253, 4, 64, 131, 52, 58, 48, 106, 97, 134, 146, 85, 107, 79, 199, 237, 198, 192, 253, 122, 145, 107, 228, 57, 171, 191, 113, 91, 13, 1, 12, 73, 191, 81, 183, 80, 66, 112, 61, 240, 65, 112, 176, 153, 86, 99, 39, 80, 33, 113, 51, 223, 232, 213, 89, 163, 6, 21, 234, 14, 3, 20, 138, 61, 209, 133, 0, 126, 190, 139, 146, 16, 135, 77, 239, 23, 188, 126, 75, 9, 66, 122, 253, 43, 120, 2, 191, 41, 1, 50, 53, 54, 254, 48, 106, 67, 76, 121, 238, 113, 99, 143, 95, 119, 14, 79, 126, 74, 22, 173, 80, 218, 6, 174, 76, 111, 33, 122, 47, 224, 117, 252, 247, 63, 198, 231, 116, 236, 238, 4, 6, 175, 106, 89, 72, 115, 245, 71, 161, 248, 223, 126, 22, 28, 170, 167, 237, 253, 92, 172, 3, 150, 122, 87, 30, 71, 141, 76, 248, 36, 47, 196, 33, 77, 137, 0, 0, 32 +}; +const uint8_t data_exo_hwmf_cross[49] PROGMEM_FAR = { +74, 40, 76, 20, 0, 0, 0, 0, 0, 4, 160, 2, 74, 8, 206, 24, 0, 0, 0, 0, 0, 0, 8, 160, 0, 0, 32, 44, 14, 233, 223, 32, 32, 198, 95, 105, 160, 30, 151, 198, 43, 227, 16, 7, 252, 24, 0, 0, 16 +}; +const uint8_t data_exo_hwmf_cube[61] PROGMEM_FAR = { +74, 72, 193, 0, 36, 160, 0, 0, 0, 0, 0, 0, 38, 128, 2, 0, 0, 0, 0, 0, 2, 38, 138, 224, 2, 32, 32, 1, 11, 111, 1, 247, 110, 78, 116, 103, 191, 79, 67, 119, 80, 76, 247, 222, 227, 106, 126, 122, 111, 125, 245, 76, 254, 122, 78, 32, 0, 16, 0, 0, 64 +}; +const uint8_t data_exo_hwmf_nameoverlay[122] PROGMEM_FAR = { +42, 64, 232, 8, 192, 0, 0, 0, 2, 74, 0, 64, 10, 200, 14, 22, 0, 0, 0, 0, 0, 0, 8, 128, 2, 32, 32, 3, 161, 8, 245, 1, 18, 4, 64, 194, 73, 247, 23, 245, 5, 128, 16, 199, 233, 223, 77, 95, 105, 96, 184, 8, 58, 82, 104, 60, 245, 41, 92, 130, 114, 13, 1, 12, 191, 253, 92, 58, 104, 51, 60, 254, 74, 67, 6, 21, 14, 3, 20, 73, 28, 58, 102, 92, 239, 36, 9, 105, 78, 206, 102, 46, 238, 121, 74, 87, 14, 10, 122, 101, 42, 119, 80, 196, 14, 116, 236, 238, 160, 201, 237, 197, 253, 106, 77, 77, 111, 22, 122, 160, 0, 8 +}; +const uint8_t data_exo_hwmf_path_cross[45] PROGMEM_FAR = { +8, 64, 110, 20, 0, 0, 0, 0, 2, 128, 0, 8, 4, 104, 206, 20, 0, 0, 0, 0, 0, 0, 0, 0, 2, 128, 0, 62, 20, 15, 112, 128, 3, 2, 169, 240, 16, 194, 1, 128, 15, 248, 84, 0, 1 +}; +const uint8_t data_exo_hwmf_path_cube[44] PROGMEM_FAR = { +166, 10, 42, 24, 2, 0, 0, 0, 0, 128, 2, 128, 0, 36, 104, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 81, 15, 1, 142, 108, 140, 36, 168, 56, 59, 192, 4, 192, 0, 16 +}; +const uint8_t data_exo_hwmf_path_rmcc[61] PROGMEM_FAR = { +10, 32, 161, 0, 0, 0, 0, 0, 0, 132, 8, 2, 74, 12, 174, 16, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 47, 10, 15, 56, 128, 5, 168, 28, 58, 145, 35, 51, 134, 145, 40, 35, 96, 133, 224, 66, 105, 168, 5, 140, 178, 8, 192, 62, 38, 6, 192, 0, 0, 128 +}; +const uint8_t data_exo_intro1[56] PROGMEM_FAR = { +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 100, 77, 254, 101, 97, 110, 119, 104, 105, 108, 206, 32, 193, 162, 238, 83, 38, 114, 98, 170, 111, 95, 107, 187, 121, 46, 33, 0, 0, 0, 64 +}; +const uint8_t data_exo_intro2[72] PROGMEM_FAR = { +4, 64, 96, 0, 0, 0, 0, 0, 0, 2, 64, 4, 96, 0, 2, 96, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 84, 245, 104, 101, 32, 114, 108, 97, 108, 173, 112, 219, 116, 121, 117, 105, 115, 111, 111, 117, 99, 188, 100, 101, 46, 10, 14, 109, 110, 108, 80, 6, 211, 111, 207, 102, 97, 39, 80, 201, 191, 46, 0, 0, 0, 64 +}; +const uint8_t data_exo_paths[422] PROGMEM_FAR = { +34, 0, 68, 34, 0, 0, 0, 0, 4, 38, 134, 8, 106, 200, 170, 225, 40, 66, 132, 104, 72, 160, 198, 78, 0, 34, 28, 32, 136, 7, 194, 80, 112, 9, 248, 15, 13, 8, 6, 212, 14, 204, 35, 144, 13, 9, 164, 10, 60, 6, 42, 182, 7, 11, 45, 15, 88, 217, 112, 52, 19, 85, 35, 170, 11, 145, 75, 20, 207, 11, 14, 5, 10, 147, 13, 234, 161, 80, 160, 82, 26, 83, 166, 34, 9, 202, 38, 101, 11, 100, 5, 35, 85, 90, 129, 80, 74, 70, 145, 147, 35, 55, 187, 6, 15, 9, 14, 15, 75, 57, 4, 35, 128, 101, 135, 0, 21, 137, 12, 226, 176, 49, 195, 80, 12, 194, 224, 46, 103, 116, 224, 51, 110, 150, 76, 197, 224, 5, 236, 104, 224, 150, 33, 171, 157, 148, 217, 154, 5, 147, 140, 246, 14, 201, 105, 13, 29, 21, 38, 251, 7, 141, 9, 34, 100, 80, 58, 112, 244, 16, 209, 1, 226, 194, 22, 0, 18, 100, 11, 168, 231, 93, 158, 8, 96, 191, 34, 113, 100, 7, 215, 10, 173, 253, 227, 103, 232, 79, 46, 74, 140, 86, 50, 219, 12, 67, 171, 77, 113, 113, 186, 0, 36, 9, 199, 235, 50, 226, 10, 5, 204, 237, 68, 20, 114, 178, 47, 49, 121, 209, 222, 191, 204, 248, 80, 103, 56, 9, 6, 207, 74, 193, 162, 62, 3, 154, 62, 109, 0, 111, 44, 35, 194, 151, 75, 108, 81, 216, 209, 158, 108, 85, 68, 5, 245, 15, 34, 45, 11, 103, 6, 123, 133, 123, 59, 178, 155, 48, 15, 125, 58, 145, 172, 145, 192, 61, 185, 11, 13, 218, 172, 93, 17, 45, 112, 118, 142, 0, 200, 112, 195, 79, 142, 157, 227, 135, 112, 86, 107, 177, 97, 98, 43, 162, 152, 228, 32, 132, 7, 175, 166, 13, 228, 55, 7, 116, 81, 199, 14, 232, 246, 5, 178, 170, 17, 198, 177, 115, 195, 212, 5, 240, 125, 167, 220, 5, 187, 217, 100, 140, 30, 124, 112, 158, 130, 183, 84, 228, 101, 166, 4, 98, 176, 2, 132, 11, 225, 57, 35, 54, 217, 73, 13, 204, 252, 251, 154, 7, 170, 64, 22, 28, 188, 13, 253, 19, 198, 163, 12, 14, 56, 240, 13, 142, 24, 208, 225, 35, 232, 233, 128, 42, 246, 112, 129, 51, 132, 119, 35, 133, 56, 98, 160, 1, 100, 3, 142, 219, 140, 184, 0, 1 +}; +const uint8_t data_exo_specs[317] PROGMEM_FAR = { +0, 2, 96, 98, 0, 0, 0, 0, 8, 8, 108, 132, 96, 64, 140, 230, 4, 8, 66, 38, 104, 8, 172, 228, 68, 102, 67, 255, 8, 5, 3, 11, 9, 14, 7, 32, 95, 1, 18, 200, 4, 23, 205, 100, 46, 40, 10, 98, 66, 15, 57, 58, 119, 65, 35, 202, 21, 217, 175, 77, 123, 7, 110, 243, 67, 80, 137, 85, 108, 20, 13, 228, 12, 170, 84, 137, 123, 50, 53, 54, 48, 215, 52, 0, 107, 49, 23, 90, 72, 26, 215, 40, 186, 86, 82, 149, 56, 188, 2, 9, 20, 41, 216, 184, 162, 69, 105, 165, 11, 39, 47, 23, 174, 44, 41, 132, 98, 79, 236, 161, 83, 247, 15, 21, 14, 4, 254, 3, 8, 9, 16, 91, 78, 102, 202, 5, 104, 128, 179, 42, 246, 19, 20, 74, 84, 135, 27, 18, 173, 48, 80, 138, 87, 123, 86, 139, 4, 56, 15, 2, 156, 108, 188, 171, 76, 68, 120, 102, 38, 20, 83, 6, 1, 169, 3, 58, 25, 23, 14, 247, 73, 162, 80, 163, 252, 67, 83, 200, 129, 14, 40, 23, 18, 91, 5, 155, 45, 174, 12, 25, 41, 10, 107, 225, 68, 77, 65, 73, 150, 133, 6, 148, 6, 212, 124, 241, 248, 92, 200, 45, 205, 37, 201, 208, 108, 134, 62, 52, 56, 119, 10, 24, 185, 51, 50, 42, 16, 209, 44, 104, 46, 23, 64, 170, 42, 130, 249, 73, 14, 130, 124, 168, 13, 123, 202, 179, 79, 167, 39, 5, 99, 21, 177, 12, 112, 232, 231, 221, 149, 230, 10, 87, 225, 113, 252, 103, 224, 72, 159, 227, 173, 116, 19, 21, 6, 21, 174, 3, 200, 241, 0, 78, 154, 5, 252, 1, 6, 190, 207, 13, 15, 83, 63, 81, 89, 251, 54, 52, 55, 51, 56, 160, 28, 16, 255, 0, 4 +}; +const uint8_t data_exo_theend[138] PROGMEM_FAR = { +0, 70, 34, 70, 0, 0, 0, 0, 2, 36, 2, 128, 36, 104, 0, 64, 0, 34, 100, 138, 42, 36, 104, 2, 96, 68, 32, 11, 15, 72, 97, 114, 100, 146, 119, 214, 101, 77, 191, 108, 102, 117, 110, 99, 233, 116, 105, 111, 40, 10, 7, 122, 65, 107, 109, 143, 222, 114, 68, 87, 147, 116, 208, 108, 89, 101, 122, 100, 142, 109, 169, 47, 102, 41, 110, 6, 31, 82, 101, 118, 105, 115, 19, 23, 50, 48, 34, 95, 208, 45, 4, 33, 10, 255, 83, 158, 104, 234, 105, 110, 103, 96, 252, 99, 100, 61, 104, 160, 116, 185, 112, 58, 160, 47, 101, 197, 103, 117, 98, 159, 46, 218, 109, 181, 102, 101, 161, 110, 52, 66, 175, 219, 119, 106, 0, 0, 0, 128 +}; +// Generated via: E:\code\demo\avrdemo\demo1\data\glfconvert.exe 3x5.glf greets.txt + +const unsigned char data_3x5_glf::data[213] PROGMEM_FAR = +{ +66,0,2,64,0,0,0,0,66,0,70,104,74,100,202,142,98,38,0,32,192,100,132,132,68,96,5,232,7,0,4,122, +17,19,5,23,138,56,201,108,31,2,42,52,108,5,49,144,171,36,160,186,186,34,119,51,45,33,20,81,137,76,17,34, +92,19,172,48,250,241,26,90,33,18,25,48,106,47,33,165,32,157,176,174,77,80,219,10,219,16,152,101,85,18,232,177, +50,177,35,155,119,157,176,157,54,95,51,115,56,67,135,0,83,14,96,120,72,203,143,28,61,99,222,55,12,96,167,34, +20,203,188,83,135,18,34,123,210,143,82,41,47,139,249,242,86,56,75,108,147,21,87,33,229,93,17,74,4,172,49,95, +199,106,141,38,73,92,35,168,23,198,205,25,244,143,245,148,106,229,26,238,228,209,9,166,108,72,114,19,108,89,69,18, +235,169,22,247,159,95,24,176,77,55,55,221,57,170,186,171,40,92,0,0,128 +}; + +const uint8_t data_3x5_glf::usedch[58] PROGMEM_FAR = +{ +32,33,45,46,48,49,50,53,54,57,65,66,67,68,70,71,72,73,74,75,76,77,78,79,80,82,83,84,85,86,87,88, +89,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,120,121,122 +}; + +const uint16_t data_3x5_glf::offsets[58] PROGMEM_FAR = +{ +0,3,10,14,19,28,35,42,49,56,63,74,84,92,103,111,121,133,141,149,161,169,181,193,204,213,224,232,240,252,264,276, +288,298,305,314,320,329,335,343,349,359,366,373,383,391,398,406,413,419,425,431,437,445,453,461,469,476 +}; + +// Generated via: E:\code\demo\avrdemo\demo1\data\glfconvert.exe topaz.glf guru.txt + +const unsigned char data_topaz_glf::data[268] PROGMEM_FAR = +{ +34,64,36,0,0,0,0,0,0,40,10,166,104,160,232,16,0,98,0,128,64,168,4,14,4,64,5,255,18,47,81,36, +0,8,16,33,68,35,65,225,114,113,34,49,49,37,45,255,92,1,140,123,47,68,38,2,57,170,66,200,35,34,44,49, +104,98,216,50,33,153,198,67,68,21,27,158,52,53,36,158,37,72,220,147,51,53,160,68,221,154,245,231,37,38,69,10, +208,65,161,83,228,37,151,132,152,96,98,233,82,142,83,107,97,54,110,112,113,212,221,239,70,68,100,18,80,113,2,128, +71,23,33,39,101,60,44,35,200,139,8,21,125,96,192,160,35,21,17,72,107,137,49,3,229,214,17,68,106,40,128,216, +242,154,79,19,138,39,213,172,238,51,82,194,158,137,52,226,31,59,191,242,206,109,19,227,2,157,0,173,206,171,38,172, +71,77,217,36,206,19,232,37,241,26,100,63,6,203,64,243,145,50,142,2,121,95,143,18,188,17,11,192,160,149,60,63, +17,168,7,68,17,6,100,103,19,83,70,127,170,82,123,59,20,23,234,85,185,36,196,168,17,72,253,73,244,198,18,47, +193,226,67,81,30,163,189,9,3,224,0,8 +}; + +const uint8_t data_topaz_glf::usedch[29] PROGMEM_FAR = +{ +32,35,45,46,48,50,52,57,66,69,70,71,72,77,80,97,99,100,101,102,105,108,110,111,114,115,116,117,119 +}; + +const uint16_t data_topaz_glf::offsets[29] PROGMEM_FAR = +{ +0,6,33,39,47,72,93,114,135,160,181,200,225,254,285,306,324,342,369,385,404,420,437,459,479,499,513,532,556 +}; + +// Generated via: E:\code\demo\avrdemo\demo1\data\glfconvert.exe vga.glf intro1.txt intro2.txt credits.txt theend.txt + +const unsigned char data_vga_glf::data[368] PROGMEM_FAR = +{ +66,32,32,0,0,0,0,0,38,66,132,106,138,196,230,193,34,64,96,134,0,70,162,14,0,64,5,254,18,0,9,16, +47,69,39,133,38,134,238,127,48,86,78,39,196,106,31,27,174,11,23,19,63,244,5,37,33,35,42,34,26,17,110,33, +135,160,151,53,101,245,95,4,51,98,151,35,140,114,232,106,68,40,53,155,83,197,125,7,54,69,36,33,106,213,115,38, +110,70,122,24,105,18,19,144,103,6,55,11,114,81,24,90,7,79,5,208,36,34,162,20,176,161,75,85,188,68,29,3, +93,205,36,83,13,33,226,118,127,3,61,19,30,17,69,37,144,137,197,133,219,65,118,203,59,52,17,204,29,146,33,147, +134,83,6,188,38,149,204,114,141,79,3,83,144,43,19,51,114,117,244,50,49,114,11,20,12,34,197,96,104,227,196,57, +111,3,212,116,83,197,86,81,50,11,116,52,56,40,2,207,4,99,218,19,33,178,112,74,70,80,91,61,84,53,54,85, +179,0,169,222,31,40,84,113,219,52,33,245,149,3,152,71,44,127,83,120,187,31,54,123,28,173,63,6,76,185,81,185, +205,70,226,130,163,60,104,18,239,70,38,55,115,215,31,251,6,253,84,39,249,163,148,163,33,147,52,232,55,215,47,169, +63,157,94,53,155,179,218,68,69,13,148,47,136,116,208,169,30,207,33,115,199,146,239,152,187,30,49,3,231,50,104,28, +246,21,69,161,114,70,134,84,123,70,27,149,246,165,22,171,31,98,249,101,105,111,243,118,162,143,14,112,85,10,2,98, +89,140,26,115,114,35,27,0,135,99,39,174,38,192,0,16 +}; + +const uint8_t data_vga_glf::usedch[46] PROGMEM_FAR = +{ +32,44,45,46,47,48,50,51,52,58,65,67,68,70,71,72,74,76,77,79,82,83,84,88,97,98,99,100,101,102,103,104, +105,107,108,109,110,111,112,114,115,116,117,118,119,121 +}; + +const uint16_t data_vga_glf::offsets[46] PROGMEM_FAR = +{ +0,3,11,16,22,34,58,74,90,106,115,136,154,176,194,215,238,255,271,294,316,338,356,372,392,407,426,439,459,472,488,509, +530,544,563,577,598,616,632,652,666,679,694,712,729,749 +}; + +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe -b 1 0 -s 5 cityscroll1.gif + +const unsigned short data_cityscroll1_gif::pal[16] PROGMEM_FAR = +{ +29548,0,44275,48599,46717,64106,57050,63463,19291,61342,57051,65370,47841,46026,44110,56851 +}; + +const unsigned char data_cityscroll1_gif::data[25359] PROGMEM_FAR = +{ +25,3,0,13,11,11,14,11,10,11,10,10,9,7,10,12,9,9,12,9,10,11,10,9,12,13,9,10,13,9,10,11, +7,8,9,11,13,12,11,6,9,9,4,8,14,13,7,13,13,9,9,13,13,11,12,10,12,15,17,17,20,18,16,17, +17,16,15,14,13,14,13,13,13,10,12,9,8,9,12,15,11,12,14,12,13,10,7,11,15,13,9,11,13,8,14,17, +12,13,15,16,12,15,21,13,15,13,17,20,16,23,19,21,21,15,19,17,19,16,17,17,17,15,16,17,15,14,16,20, +15,15,15,16,19,15,12,11,18,16,12,15,14,13,15,11,13,14,16,16,15,18,16,16,14,15,16,15,14,17,15,15, +12,13,12,11,10,9,5,5,4,5,5,4,5,4,6,1,1,5,4,5,5,5,6,5,7,7,7,8,10,11,11,6, +4,6,9,4,4,4,7,6,9,7,7,4,6,6,13,12,11,8,6,11,8,8,6,8,9,7,4,7,7,8,6,7, +8,7,7,6,8,6,4,9,7,5,10,6,8,6,5,2,5,4,7,5,7,7,7,13,13,17,19,17,16,18,27,28, +26,32,27,27,27,26,25,31,30,19,26,25,26,23,20,17,14,18,16,16,12,11,14,10,9,10,8,7,9,10,8,11, +11,12,13,10,11,8,10,9,10,6,6,7,4,7,6,1,1,5,1,7,4,4,5,5,5,7,6,4,5,6,4,6, +4,7,7,9,7,7,7,10,8,9,8,10,7,7,6,6,6,8,7,6,8,7,7,6,7,8,6,8,9,8,6,7, +7,6,7,6,5,5,3,4,7,8,11,13,12,10,9,8,10,10,7,7,8,7,10,10,10,9,10,10,12,10,10,8, +9,8,9,10,9,11,7,10,11,12,13,13,18,10,9,11,9,12,11,11,12,9,14,16,16,16,16,19,18,25,21,22, +16,22,22,28,22,20,22,23,25,22,22,22,22,26,24,16,28,23,30,21,25,25,26,27,23,26,20,19,15,22,15,14, +12,17,18,24,17,16,16,22,16,19,17,21,13,15,13,12,18,17,16,19,12,18,13,9,12,15,16,18,15,19,17,20, +18,15,19,17,18,18,18,19,24,16,17,12,13,19,12,13,15,17,17,20,14,8,14,15,14,14,15,16,19,16,16,22, +15,14,16,12,13,13,14,13,15,15,12,15,14,12,15,17,17,15,16,12,10,15,15,19,16,13,14,14,11,10,10,8, +9,9,4,4,7,8,8,6,9,6,13,6,9,11,10,10,10,13,9,13,11,11,9,9,9,3,10,10,15,12,16,17, +16,20,11,10,16,11,19,17,20,18,15,16,18,16,10,16,13,10,11,13,15,15,10,9,11,8,5,10,10,7,7,8, +7,9,9,12,8,10,10,5,6,9,6,8,7,9,9,10,10,10,13,13,11,10,10,16,14,14,14,15,14,15,14,11, +14,13,12,8,10,13,12,9,10,11,12,13,9,12,10,6,9,9,11,9,10,11,8,8,8,10,8,8,7,8,7,8, +11,10,7,9,7,7,7,7,7,6,6,7,5,5,5,6,5,8,9,7,7,6,7,7,5,5,7,7,8,12,7,9, +9,11,9,10,11,9,10,7,10,7,4,12,14,11,14,18,17,20,23,22,28,22,26,25,30,23,19,18,23,20,20,18, +18,18,16,15,11,11,14,9,12,10,9,7,10,9,8,13,13,12,12,13,10,12,13,12,9,9,7,6,9,9,7,9, +8,9,11,11,7,9,10,12,13,12,12,13,12,10,12,12,11,12,13,11,11,8,11,8,13,10,9,12,0,0,29,0, +53,0,76,0,105,0,130,0,152,0,177,0,201,0,223,0,244,0,5,1,29,1,59,1,80,1,101,1,129,1,149,1, +172,1,197,1,223,1,244,1,14,2,43,2,63,2,85,2,113,2,133,2,154,2,178,2,193,2,212,2,232,2,0,3, +28,3,53,3,76,3,89,3,109,3,129,3,139,3,159,3,190,3,220,3,237,3,10,4,40,4,60,4,83,4,113,4, +143,4,170,4,198,4,222,4,253,4,32,5,69,5,108,5,151,5,190,5,223,5,3,6,41,6,76,6,109,6,140,6, +170,6,200,6,229,6,1,7,30,7,55,7,82,7,100,7,120,7,142,7,172,7,204,7,229,7,254,7,29,8,57,8, +85,8,109,8,125,8,150,8,183,8,212,8,232,8,0,9,30,9,48,9,80,9,117,9,146,9,175,9,206,9,238,9, +10,10,43,10,86,10,115,10,150,10,178,10,217,10,9,11,48,11,101,11,148,11,200,11,251,11,27,12,70,12,112,12, +155,12,193,12,231,12,14,13,50,13,82,13,116,13,156,13,192,13,225,13,8,14,56,14,90,14,121,14,152,14,192,14, +232,14,14,15,43,15,70,15,109,15,147,15,173,15,204,15,237,15,13,16,50,16,75,16,109,16,142,16,178,16,218,16, +0,17,44,17,83,17,122,17,156,17,192,17,230,17,11,18,44,18,79,18,111,18,142,18,170,18,200,18,227,18,251,18, +18,19,38,19,49,19,60,19,68,19,80,19,92,19,101,19,113,19,121,19,135,19,137,19,139,19,151,19,162,19,175,19, +188,19,200,19,214,19,227,19,242,19,1,20,16,20,34,20,54,20,77,20,102,20,117,20,126,20,140,20,160,20,170,20, +180,20,190,20,209,20,223,20,245,20,5,21,21,21,30,21,45,21,60,21,89,21,117,21,144,21,163,21,177,21,204,21, +225,21,247,21,6,22,25,22,47,22,66,22,77,22,95,22,115,22,138,22,152,22,168,22,189,22,207,22,225,22,240,22, +3,23,15,23,25,23,46,23,64,23,78,23,100,23,116,23,136,23,151,23,163,23,168,23,181,23,191,23,208,23,219,23, +236,23,253,23,14,24,48,24,80,24,120,24,161,24,198,24,239,24,22,25,80,25,148,25,210,25,32,26,96,26,158,26, +228,26,41,27,110,27,178,27,255,27,49,28,119,28,184,28,249,28,48,29,100,29,143,29,177,29,224,29,4,30,41,30, +70,30,97,30,127,30,151,30,173,30,198,30,216,30,234,30,255,30,22,31,42,31,68,31,93,31,120,31,149,31,172,31, +198,31,217,31,240,31,6,32,30,32,43,32,61,32,78,32,87,32,104,32,120,32,122,32,124,32,138,32,140,32,156,32, +165,32,174,32,185,32,196,32,206,32,224,32,238,32,248,32,3,33,15,33,23,33,36,33,44,33,61,33,77,33,97,33, +114,33,131,33,148,33,171,33,189,33,210,33,229,33,252,33,12,34,28,34,41,34,56,34,70,34,88,34,103,34,115,34, +133,34,148,34,164,34,178,34,193,34,212,34,227,34,247,34,14,35,36,35,50,35,68,35,87,35,101,35,121,35,135,35, +146,35,156,35,162,35,171,35,189,35,209,35,234,35,7,36,35,36,60,36,82,36,102,36,125,36,148,36,164,36,182,36, +201,36,218,36,241,36,10,37,35,37,56,37,80,37,105,37,132,37,157,37,180,37,199,37,219,37,237,37,1,38,22,38, +41,38,64,38,80,38,102,38,127,38,154,38,185,38,216,38,255,38,21,39,41,39,65,39,84,39,111,39,136,39,161,39, +190,39,210,39,243,39,22,40,59,40,95,40,134,40,178,40,220,40,24,41,72,41,122,41,164,41,216,41,17,42,86,42, +142,42,191,42,246,42,47,43,110,43,171,43,230,43,22,44,80,44,147,44,205,44,248,44,52,45,102,45,170,45,225,45, +37,46,101,46,163,46,225,46,28,47,86,47,141,47,189,47,229,47,23,48,63,48,102,48,133,48,180,48,226,48,20,49, +58,49,97,49,134,49,180,49,221,49,13,50,55,50,100,50,133,50,170,50,202,50,229,50,10,51,46,51,79,51,123,51, +152,51,192,51,223,51,244,51,17,52,52,52,92,52,137,52,173,52,218,52,6,53,51,53,94,53,130,53,174,53,216,53, +5,54,52,54,95,54,145,54,199,54,236,54,22,55,52,55,85,55,129,55,162,55,194,55,229,55,17,56,53,56,101,56, +137,56,155,56,190,56,223,56,253,56,33,57,76,57,120,57,167,57,209,57,248,57,44,58,84,58,121,58,165,58,197,58, +233,58,14,59,49,59,84,59,122,59,158,59,191,59,232,59,11,60,43,60,82,60,120,60,163,60,201,60,239,60,15,61, +42,61,80,61,120,61,162,61,201,61,234,61,9,62,43,62,72,62,97,62,125,62,146,62,170,62,193,62,204,62,214,62, +232,62,252,62,14,63,29,63,51,63,64,63,92,63,107,63,132,63,160,63,187,63,215,63,242,63,17,64,40,64,72,64, +98,64,123,64,145,64,169,64,190,64,196,64,219,64,245,64,28,65,58,65,103,65,150,65,190,65,236,65,8,66,30,66, +69,66,98,66,143,66,184,66,232,66,18,67,55,67,94,67,134,67,171,67,196,67,232,67,10,68,37,68,64,68,91,68, +130,68,165,68,189,68,209,68,237,68,1,69,13,69,36,69,61,69,80,69,98,69,120,69,139,69,160,69,184,69,210,69, +231,69,1,70,26,70,38,70,55,70,77,70,92,70,112,70,128,70,149,70,173,70,197,70,222,70,251,70,28,71,64,71, +92,71,117,71,144,71,183,71,221,71,2,72,40,72,75,72,113,72,155,72,193,72,220,72,255,72,29,73,61,73,81,73, +105,73,139,73,172,73,196,73,221,73,251,73,28,74,61,74,86,74,115,74,141,74,158,74,183,74,206,74,230,74,250,74, +21,75,45,75,68,75,88,75,110,75,135,75,156,75,175,75,193,75,213,75,230,75,249,75,18,76,41,76,59,76,81,76, +100,76,117,76,136,76,153,76,171,76,186,76,201,76,217,76,231,76,245,76,3,77,20,77,34,77,55,77,76,77,96,77, +114,77,129,77,148,77,167,77,180,77,192,77,210,77,230,77,251,77,22,78,37,78,57,78,79,78,103,78,123,78,148,78, +174,78,194,78,220,78,237,78,6,79,23,79,32,79,62,79,96,79,123,79,160,79,199,79,245,79,37,80,94,80,148,80, +209,80,8,81,75,81,135,81,207,81,7,82,56,82,108,82,159,82,204,82,1,83,49,83,100,83,145,83,186,83,219,83, +246,83,20,84,52,84,76,84,104,84,132,84,156,84,173,84,197,84,221,84,241,84,18,85,47,85,77,85,108,85,143,85, +169,85,203,85,237,85,13,86,38,86,60,86,79,86,96,86,121,86,143,86,163,86,185,86,206,86,230,86,1,87,27,87, +43,87,65,87,90,87,120,87,154,87,183,87,214,87,249,87,23,88,48,88,80,88,112,88,142,88,172,88,206,88,236,88, +10,89,32,89,59,89,78,89,113,89,138,89,163,89,32,47,0,3,1,32,154,2,5,1,7,0,3,1,8,0,4,1, +5,0,27,1,32,42,2,3,1,12,13,32,46,0,4,1,194,30,4,1,7,0,196,5,198,35,3,0,32,35,1,26, +2,196,27,194,25,196,53,196,22,65,0,200,50,2,0,196,9,65,0,32,35,1,20,2,196,50,196,24,196,67,65,0, +3,1,196,47,196,7,10,0,3,1,196,83,65,1,2,0,32,36,1,3,2,196,79,32,45,0,12,1,32,145,2,196, +57,197,1,105,196,39,9,0,198,87,196,111,28,1,9,13,196,26,7,1,32,138,2,198,60,8,0,196,81,2,0,196, +64,14,0,32,36,1,32,45,0,3,1,2,0,21,1,32,131,2,196,104,196,30,2,0,196,81,32,41,0,17,1,198, +26,196,88,18,1,32,123,2,2,1,9,0,198,76,197,1,128,182,32,41,0,9,1,198,50,15,0,21,1,32,115,2, +196,74,196,128,135,196,30,65,0,196,50,18,0,196,23,196,47,32,107,2,196,19,3,1,3,0,8,1,198,128,151,32, +59,0,196,44,196,44,32,100,2,200,22,13,1,198,128,246,32,51,0,196,61,22,0,21,1,32,93,2,197,1,129,4, +65,0,2,1,196,45,23,1,32,60,0,32,44,0,196,128,211,32,39,0,21,1,32,85,2,196,114,196,129,34,196,73, +3,1,11,3,19,1,32,58,0,198,31,7,0,21,1,32,77,2,196,128,188,198,24,9,3,13,1,32,55,0,196,22, +196,22,32,70,2,196,128,207,196,43,65,1,24,3,10,1,32,53,0,32,43,0,196,73,196,100,65,1,32,62,2,196, +128,233,3,1,4,0,4,1,28,3,8,1,32,50,0,196,29,30,0,21,1,32,55,2,200,26,196,49,5,3,8,1, +32,48,0,196,21,196,91,32,48,2,196,128,198,196,115,2,0,5,1,31,3,8,1,32,45,0,196,44,196,128,228,32, +40,2,198,25,65,0,6,1,23,3,4,1,4,3,8,1,32,43,0,32,146,0,21,1,32,33,2,196,128,247,196,129, +23,198,129,122,2,1,22,3,18,1,32,41,0,32,153,0,21,1,26,2,196,26,198,129,221,2,1,21,3,20,1,32, +39,0,32,160,0,22,1,2,2,3,1,13,2,198,26,2,1,14,3,10,1,65,8,15,1,32,38,0,32,168,0,21, +1,11,2,196,73,196,130,12,2,1,196,103,65,1,196,99,196,30,5,8,12,1,32,36,0,32,175,0,196,56,65,2, +198,30,3,0,27,1,10,8,11,1,32,33,0,32,180,0,5,1,65,3,16,1,10,0,196,129,32,20,1,26,8,9, +1,31,0,32,178,0,6,1,5,3,13,1,10,0,2,1,9,0,3,1,4,0,17,1,32,35,8,9,1,29,0,32, +177,0,196,29,3,3,11,1,200,29,14,1,32,45,8,9,1,26,0,32,175,0,196,21,4,3,9,1,196,49,3,0, +196,43,16,8,12,1,23,0,32,174,0,6,1,15,3,8,1,7,0,19,1,32,57,8,3,1,2,3,9,1,20,0, +32,173,0,5,1,20,3,31,1,198,21,196,18,18,0,32,171,0,196,129,13,196,82,15,1,32,58,8,196,37,196,71, +15,0,32,170,0,5,1,29,3,24,1,32,59,8,3,1,9,3,9,1,12,0,32,168,0,196,129,52,10,3,13,1, +65,6,5,3,2,1,198,28,3,3,196,92,3,0,32,167,0,196,25,7,3,7,1,6,3,2,1,32,33,8,2,1, +24,8,3,1,13,3,11,1,6,0,32,165,0,196,29,6,3,3,1,200,29,65,1,23,8,196,24,3,1,65,8,9, +1,4,0,32,164,0,196,26,65,3,200,26,65,8,2,1,13,3,3,1,4,8,9,1,65,0,32,162,0,196,24,2, +3,204,24,3,8,7,1,32,160,0,196,86,32,44,3,2,1,7,3,198,91,202,65,8,8,5,1,32,160,0,196,129, +249,25,3,2,1,196,87,196,109,196,21,3,8,2,1,196,130,98,32,109,0,206,24,2,8,196,129,59,32,120,0,196, +126,32,41,3,198,34,198,128,135,3,1,14,8,15,1,32,145,0,3,1,32,53,3,3,1,6,3,3,1,32,32,8, +3,1,24,8,3,1,13,3,2,1,15,8,4,2,17,1,32,139,0,196,128,178,32,36,3,7,1,5,3,196,35,65, +8,196,57,9,8,198,128,173,14,8,9,2,196,130,5,32,91,0,196,130,109,18,3,10,1,206,31,15,2,17,1,32, +127,0,3,1,32,45,3,20,1,32,33,8,3,1,22,8,4,1,13,3,3,1,15,8,21,2,17,1,32,120,0,196, +130,154,15,3,7,1,4,8,196,129,249,28,8,196,34,196,128,247,65,1,15,8,27,2,17,1,32,114,0,196,130,184, +13,3,196,31,2,8,200,31,16,8,32,32,2,196,130,102,32,66,0,196,130,163,8,3,196,23,32,45,8,204,82,65, +8,32,38,2,17,1,32,102,0,4,1,32,38,3,6,1,32,54,8,4,1,21,8,3,1,14,3,3,1,16,8,32, +44,2,17,1,32,95,0,5,1,32,37,3,6,1,32,55,8,5,1,19,8,4,1,14,3,2,1,17,8,32,50,2, +17,1,32,88,0,196,129,173,3,3,196,26,32,39,8,4,1,196,31,198,128,162,2,8,32,56,2,17,1,32,82,0, +196,131,16,5,3,196,28,2,8,4,1,17,8,198,57,196,128,158,2,8,32,62,2,17,1,32,75,0,7,1,32,34, +3,196,111,196,56,196,128,210,198,84,196,82,32,68,2,196,130,156,32,60,0,8,1,32,32,3,196,128,136,32,38,8, +5,1,196,129,36,65,8,198,59,65,8,32,73,2,17,1,32,63,0,4,1,65,8,196,131,82,6,3,196,128,170,32, +37,8,6,1,2,8,5,1,12,8,196,67,65,8,32,79,2,17,1,32,57,0,3,1,3,8,7,1,26,3,5,1, +32,57,8,6,1,3,8,9,1,6,8,5,1,14,3,4,1,19,8,32,85,2,17,1,32,50,0,4,1,5,8,196, +130,181,2,3,7,1,65,3,196,128,246,32,35,8,6,1,7,8,12,1,65,8,196,77,2,8,29,2,4,1,32,58, +2,16,1,32,44,0,4,1,7,8,8,1,6,3,15,1,65,3,3,1,32,55,8,7,1,10,8,15,1,13,3,5, +1,18,8,2,1,29,2,6,1,32,61,2,20,1,32,35,0,4,1,9,8,27,1,65,3,2,1,32,55,8,6,1, +18,8,15,1,6,3,5,1,15,8,6,1,196,40,196,83,5,2,21,1,27,0,196,38,3,8,196,131,59,13,8,196, +34,196,114,3,1,65,3,196,55,2,8,11,1,26,2,3,1,2,7,12,1,32,65,2,22,1,20,0,196,129,130,196, +105,28,8,196,109,14,8,196,74,14,8,14,1,196,45,65,8,198,37,3,7,13,1,32,70,2,20,1,12,0,196,129, +86,8,1,196,131,172,196,129,94,65,8,196,37,6,8,8,1,3,8,15,1,6,8,196,39,4,7,196,39,3,2,21, +1,3,0,196,128,245,65,8,12,1,196,128,139,12,1,15,8,196,129,223,24,8,16,1,11,8,196,36,5,7,13,1, +32,56,2,16,1,3,2,20,1,196,22,25,1,196,132,60,11,8,196,131,90,9,8,196,69,11,8,196,34,196,107,196, +132,119,32,32,2,25,1,6,2,13,1,27,8,32,34,1,13,8,196,70,31,8,196,32,5,8,196,65,8,7,18,1, +32,35,2,31,1,11,2,196,130,53,196,132,9,30,8,11,1,196,129,147,9,1,28,8,196,31,196,95,6,1,27,2, +9,1,17,2,10,1,8,2,196,129,207,24,8,196,132,158,22,8,196,125,22,8,196,61,11,7,19,1,20,2,6,1, +25,2,7,1,6,2,196,31,196,130,195,196,131,65,196,131,186,15,8,196,30,7,7,17,1,14,2,5,1,29,2,7, +1,3,2,196,60,196,131,143,196,129,227,196,118,22,8,196,29,7,7,15,1,8,2,4,1,32,33,2,6,1,2,2, +196,89,11,8,198,129,222,8,1,32,50,8,196,30,196,128,211,134,21,196,57,7,2,4,1,196,133,51,32,98,8,198, +129,253,196,129,138,196,55,10,7,196,55,30,2,3,1,65,2,196,129,192,32,47,8,2,1,65,8,196,131,52,25,8, +196,82,15,7,12,1,196,82,5,1,22,2,196,28,198,22,3,8,196,100,18,7,32,32,1,14,2,196,131,36,32,50, +8,196,131,42,14,8,196,120,17,7,32,37,1,10,2,3,1,198,130,2,32,46,8,196,130,6,14,8,196,128,142,17, +7,4,1,196,129,145,2,1,196,116,196,128,201,4,1,198,130,74,32,44,8,196,129,253,15,8,196,128,172,16,7,4, +1,2,7,6,1,8,7,2,1,12,2,9,1,3,2,5,1,2,3,196,128,180,5,8,196,36,26,2,2,1,32,64, +7,3,1,198,35,3,7,15,2,15,1,198,130,134,32,43,8,196,91,196,26,32,63,7,4,1,65,7,5,1,12,7, +196,58,5,2,12,1,196,54,4,8,196,116,25,2,3,1,32,63,7,3,1,198,28,65,7,196,134,17,2,2,8,1, +3,6,3,1,196,84,3,8,196,128,146,200,32,65,7,4,1,14,7,196,134,43,196,25,2,6,3,1,198,131,149,32, +38,8,196,130,177,13,8,198,60,2,1,198,31,65,7,3,1,12,2,6,1,8,6,3,1,4,3,196,131,151,32,35, +8,196,32,196,88,32,61,7,32,35,1,3,2,5,1,10,6,3,1,198,131,177,32,34,8,196,56,198,25,3,1,15, +6,3,1,198,130,192,32,78,8,196,72,25,2,3,1,32,57,7,13,1,11,6,19,1,196,26,6,3,196,132,17,32, +36,8,196,97,25,2,3,1,32,56,7,5,1,6,6,4,1,23,6,6,1,15,6,3,1,8,3,5,1,32,91,8, +3,1,32,67,8,25,2,3,1,32,55,7,6,1,196,34,134,37,22,6,196,104,4,6,196,133,235,196,132,23,30,8, +196,33,25,2,3,1,32,54,7,198,128,151,196,112,8,6,200,29,29,8,196,53,198,21,4,1,10,6,9,1,22,6, +3,1,14,6,196,135,197,196,132,67,27,8,196,77,25,2,3,1,32,53,7,196,30,2,6,196,128,136,196,112,13,6, +196,135,225,65,3,196,132,97,26,8,196,107,202,31,65,6,196,24,12,6,196,133,6,196,129,131,14,8,196,125,25,2, +3,1,32,37,7,8,1,7,7,196,53,198,53,65,1,12,6,198,132,219,196,129,190,14,8,196,131,161,12,8,25,2, +3,1,32,33,7,16,1,3,7,3,1,15,6,14,1,17,6,5,1,11,6,3,1,15,3,7,1,32,81,8,3,1, +32,66,8,25,2,3,1,30,7,25,1,16,6,196,128,252,7,6,198,129,38,17,3,196,130,113,196,133,112,32,44,8, +25,2,2,1,29,7,9,1,196,131,77,198,33,2,6,4,1,9,6,196,63,196,135,234,32,76,8,196,62,196,30,27, +7,196,125,65,7,12,1,196,89,9,1,20,6,198,31,5,3,9,1,17,8,196,131,215,11,8,196,93,25,2,3,1, +25,7,6,1,11,7,200,34,65,6,4,1,8,6,196,122,7,3,9,1,15,8,29,1,27,8,196,125,25,2,3,1, +23,7,196,33,2,7,204,33,3,3,196,135,219,4,8,32,56,1,198,134,52,32,43,8,198,29,196,129,236,196,91,18, +6,196,128,181,4,6,3,1,198,61,6,3,11,1,5,8,6,1,196,134,136,22,1,196,38,25,2,3,1,22,7,4, +1,17,7,10,1,19,6,14,1,22,6,3,1,8,6,2,1,30,3,12,1,2,8,5,1,28,8,21,1,4,8,3, +1,32,66,8,25,2,3,1,21,7,196,44,196,129,38,7,6,15,1,21,6,200,43,4,3,196,131,111,9,8,196,129, +1,25,2,3,1,20,7,196,130,140,13,7,196,130,16,5,6,198,113,196,71,5,3,196,128,170,196,129,226,196,71,26, +8,196,129,36,25,2,3,1,19,7,196,108,10,7,196,35,14,1,11,6,198,106,196,36,196,133,162,196,131,149,198,98, +25,2,3,1,18,7,196,29,65,7,198,29,3,1,7,6,198,128,134,196,136,168,196,128,233,65,3,8,1,6,8,196, +135,3,13,8,196,129,103,25,2,196,130,187,2,7,196,130,243,11,7,2,1,198,129,143,5,6,15,1,10,6,198,51, +65,1,196,133,119,196,137,60,196,129,25,7,8,196,47,198,135,213,196,132,161,12,8,196,132,190,2,7,196,128,222,65, +7,196,129,181,3,6,200,45,196,137,210,4,1,9,3,196,133,153,196,132,226,3,1,196,137,35,196,92,65,8,196,132, +229,198,130,247,196,75,196,130,77,6,1,10,6,3,1,6,6,198,129,104,11,1,5,3,9,1,12,8,196,134,55,2, +8,3,1,12,8,2,1,14,8,2,1,196,133,59,196,135,71,4,8,196,133,26,196,131,72,20,1,20,6,196,128,132, +197,1,53,196,131,100,29,3,196,129,151,196,133,182,196,134,200,11,8,196,133,190,2,1,13,8,28,1,9,8,13,1, +3,8,196,133,73,4,1,12,7,19,1,196,131,95,196,129,106,134,100,8,6,3,1,5,6,196,129,237,196,136,170,196, +134,241,198,100,196,134,161,10,8,196,134,92,196,136,66,20,8,32,43,1,3,8,26,2,3,1,13,7,198,56,2,7, +196,131,100,4,6,196,128,236,3,1,4,6,196,130,245,196,129,26,198,129,26,32,34,8,196,134,18,5,1,10,8,196, +136,64,196,137,209,19,1,5,8,198,52,3,1,196,103,65,1,202,52,3,6,4,1,198,130,165,196,95,26,8,3,1, +13,8,3,1,196,40,32,41,8,26,2,3,1,12,7,7,1,8,7,10,1,3,7,7,1,3,7,196,131,39,6,6, +196,129,71,4,6,196,128,253,65,6,196,130,214,5,3,196,132,207,196,47,200,44,65,1,7,7,197,1,132,100,196,130, +248,8,7,196,131,231,196,84,11,1,13,6,196,40,196,39,196,135,15,196,133,134,32,39,8,196,134,232,10,8,196,43, +6,7,4,1,3,7,134,48,4,7,3,1,8,7,196,131,122,198,129,154,65,6,6,1,9,6,3,1,2,6,198,131, +42,196,47,198,129,69,32,53,8,26,2,196,34,196,121,2,1,6,7,196,42,196,88,196,132,107,196,130,244,196,129,120, +5,1,196,132,139,12,3,196,134,20,32,53,8,196,85,200,39,5,7,196,82,8,7,3,1,196,82,196,128,254,2,6, +14,1,8,6,23,1,65,6,198,132,50,196,136,96,28,8,196,135,247,5,8,200,77,4,7,196,130,248,4,7,4,1, +65,7,196,131,238,4,6,14,1,6,6,7,1,196,137,66,65,1,8,3,196,133,238,32,36,8,196,43,198,116,65,1, +5,7,7,1,16,7,5,1,196,41,196,77,3,6,9,1,16,2,8,1,6,3,196,131,190,9,8,196,135,131,10,8, +196,37,196,71,4,1,10,7,16,1,14,6,24,1,21,2,5,1,4,3,196,133,212,65,8,196,136,231,65,8,196,36, +26,2,3,1,10,7,9,1,4,7,7,1,9,7,19,1,12,6,22,1,26,2,196,134,229,22,8,196,135,190,2,8, +196,70,198,35,8,1,196,103,196,129,88,6,1,65,7,4,1,65,6,2,1,196,131,39,196,134,158,196,133,128,32,69, +8,10,1,2,8,196,137,100,196,43,196,41,5,7,198,133,236,196,128,219,198,42,26,1,32,35,2,196,136,247,11,8, +3,1,196,139,113,2,1,5,8,196,136,56,9,8,198,37,65,1,7,7,8,1,65,7,25,1,198,133,196,22,2,196, +128,240,7,8,3,1,196,139,185,198,138,11,32,41,8,196,110,196,128,138,8,7,3,1,198,39,196,132,56,7,1,6, +7,196,133,233,23,2,196,134,112,2,8,196,138,157,196,128,191,196,131,57,196,76,26,2,196,128,183,4,1,8,7,196, +129,241,3,1,7,7,6,1,5,7,6,1,196,128,235,198,49,196,129,66,6,8,196,140,109,3,3,196,138,205,196,131, +102,196,135,84,26,8,200,49,7,1,65,7,2,1,5,7,197,1,48,65,7,198,44,196,135,9,16,8,196,137,18,4, +3,7,1,65,8,196,137,40,196,35,4,1,4,7,3,1,6,7,4,1,200,81,2,1,198,122,65,2,196,76,7,1, +19,3,11,1,32,56,8,26,2,3,1,10,7,3,1,9,7,11,1,4,7,198,44,2,1,200,38,196,34,2,8,198, +68,196,138,74,2,8,204,32,65,1,196,129,105,196,130,36,198,128,139,196,130,94,196,128,182,2,2,196,128,221,3,8, +196,138,44,3,3,6,1,65,6,196,136,22,23,8,26,2,3,1,11,7,2,1,196,72,196,49,2,1,3,7,4,1, +2,7,200,51,196,74,3,8,196,140,218,2,3,5,1,6,6,8,1,32,49,8,200,41,196,130,163,196,137,11,5,7, +5,1,196,136,167,196,129,196,7,7,196,85,196,141,6,32,75,8,196,43,196,134,29,196,137,25,9,8,200,39,196,128, +144,2,1,196,130,4,198,116,198,112,196,141,28,65,3,196,134,27,65,6,196,132,222,19,8,200,130,30,196,135,14,13, +1,196,31,3,2,196,129,103,2,8,198,139,59,16,6,196,136,255,65,8,26,2,2,1,196,131,44,65,1,5,7,10, +1,13,7,14,1,6,7,8,1,196,98,4,2,196,41,6,1,9,3,196,130,114,65,6,196,132,154,26,2,2,1,13, +7,196,129,254,196,8,196,36,7,1,198,135,198,32,33,2,196,129,214,2,8,7,1,7,3,196,131,186,196,139,192,12, +8,202,39,13,1,11,7,3,1,196,65,198,128,243,65,8,5,1,6,3,196,131,122,65,6,196,30,198,65,65,7,6, +1,7,7,9,1,13,7,11,1,196,37,198,129,25,6,2,196,65,3,8,5,1,4,3,198,37,196,32,65,7,196,129, +196,196,37,196,103,196,41,198,131,21,8,7,196,137,110,65,2,196,139,102,22,8,196,133,180,196,67,196,132,90,10,7, +196,130,151,4,7,10,1,196,99,134,74,196,129,148,7,2,196,130,189,196,141,226,196,133,139,196,126,196,132,19,2,1, +196,124,198,138,52,196,38,3,1,9,7,196,64,65,2,196,129,143,196,138,154,66,1,8,196,135,101,6,6,196,128,162, +198,131,37,6,7,25,1,13,7,4,1,198,35,196,137,39,196,138,32,5,1,8,8,196,66,202,131,177,196,137,163,196, +136,49,198,129,218,32,47,2,196,137,181,12,8,27,1,13,8,66,3,1,2,3,19,6,196,128,221,200,131,211,65,1, +196,132,116,14,7,4,1,10,7,196,95,4,1,196,130,204,32,46,1,18,8,6,3,18,6,196,133,187,196,34,196,137, +61,196,132,202,3,7,198,34,65,2,3,1,2,3,32,53,1,27,8,8,3,2,1,15,6,3,1,2,8,196,138,76, +196,132,109,196,129,71,196,129,98,17,7,198,131,249,32,48,2,3,1,196,142,60,196,106,28,8,9,3,196,135,221,2, +1,3,8,196,141,7,8,8,196,128,206,196,136,240,7,7,196,133,25,65,7,197,1,137,56,196,115,198,84,29,3,196, +129,177,9,3,196,135,240,4,1,198,140,67,23,8,196,133,109,196,132,25,17,7,5,1,198,137,31,196,128,241,2,2, +196,134,75,8,3,196,131,205,21,8,2,1,9,3,198,132,153,2,1,2,2,196,139,51,13,8,196,138,225,65,7,196, +129,225,19,7,11,1,13,7,196,46,196,143,197,4,3,196,131,247,18,8,196,142,103,196,131,150,4,2,196,134,130,29, +8,196,134,75,196,129,162,19,7,196,130,11,196,131,147,11,2,196,143,193,2,3,196,132,30,16,8,4,1,12,3,15, +1,7,2,196,138,208,4,8,196,134,114,65,7,196,42,196,135,207,196,130,42,4,2,10,1,30,3,196,141,191,4,8, +198,136,44,6,1,10,2,196,138,219,196,35,196,112,7,7,200,131,143,12,7,196,129,139,65,2,14,1,28,3,32,37, +8,196,131,2,8,3,196,143,190,196,135,192,15,8,196,71,2,7,196,130,116,9,1,6,7,196,132,225,196,113,196,137, +221,196,143,254,65,3,32,36,8,196,41,4,1,16,2,196,135,230,12,8,196,39,65,7,196,130,50,6,1,198,132,150, +196,129,122,11,2,32,33,1,32,35,8,196,131,76,7,3,196,38,196,126,196,141,70,65,8,196,38,196,136,89,9,7, +10,1,196,130,102,196,133,208,196,137,248,196,129,94,31,1,30,8,198,40,9,2,11,1,14,8,26,2,3,1,22,7, +13,1,7,7,16,1,23,7,3,1,32,66,2,30,1,29,8,3,1,25,3,4,1,28,2,12,1,10,8,27,2,198, +36,20,1,25,7,196,30,23,2,196,136,153,10,8,196,135,153,65,3,196,31,2,2,3,13,12,1,6,8,27,2,3, +1,24,7,29,1,28,7,2,1,32,92,2,8,1,24,8,3,1,24,3,4,1,31,2,6,13,15,1,198,32,196,132, +201,31,7,196,29,2,2,196,136,158,10,8,196,136,191,196,139,181,3,2,10,13,11,1,198,60,196,139,24,196,135,248, +7,7,196,32,2,2,196,140,122,196,132,10,4,3,196,32,65,2,14,13,7,1,198,90,5,7,196,129,151,198,89,196, +139,122,6,1,8,8,196,134,157,65,3,198,30,5,13,3,1,196,28,2,7,196,137,53,32,34,7,196,58,7,2,23, +1,20,3,196,54,196,54,8,13,196,25,3,7,196,81,196,131,134,32,63,2,17,1,20,3,196,140,3,196,107,12,13, +196,24,32,47,7,196,43,13,2,9,1,19,3,196,140,23,4,2,22,13,200,21,5,2,196,137,196,198,112,196,14,198, +12,3,3,196,140,17,2,2,22,13,198,23,65,3,196,12,196,11,196,31,196,146,21,8,1,32,41,2,23,13,196,43, +196,130,144,196,132,174,196,55,65,13,196,55,196,140,36,10,2,23,13,196,64,2,2,196,140,180,32,43,2,23,13,196, +13,196,63,18,2,23,13,28,2,2,1,32,81,7,2,1,32,184,2,23,13,204,15,204,17,196,19,32,75,7,196,72, +32,142,2,24,13,196,128,157,196,138,71,32,183,2,24,13,196,128,168,25,7,196,134,106,32,162,2,24,13,196,128,181, +18,7,196,134,81,32,166,2,24,13,196,128,194,196,140,246,196,67,15,2,25,13,27,2,3,1,32,39,7,20,1,32, +206,2,25,13,27,2,196,138,69,198,141,48,32,186,2,25,13,27,2,2,1,28,7,17,1,32,214,2,6,1,26,13, +27,2,3,1,21,7,17,1,32,209,2,18,1,25,13,27,2,3,1,16,7,17,1,32,207,2,25,1,25,13,27,2, +198,134,191,196,128,134,24,2,23,1,5,3,2,1,26,13,27,2,3,1,4,7,196,34,18,1,8,3,3,1,6,3, +2,1,25,13,27,2,3,1,65,7,15,1,32,206,2,18,1,13,3,3,1,7,3,3,1,24,13,28,2,196,142,115, +32,182,2,16,1,5,0,2,1,6,3,2,1,5,3,198,144,185,25,13,28,2,196,142,208,32,146,2,196,146,41,65, +0,204,27,32,234,2,17,1,17,0,204,36,32,226,2,196,142,118,10,0,196,42,3,1,198,44,32,219,2,196,142,166, +6,0,3,1,5,3,3,1,4,3,196,60,65,13,32,210,2,23,1,32,34,0,204,21,32,201,2,24,1,32,42,0, +204,31,32,192,2,25,1,32,50,0,204,41,32,186,2,196,51,32,33,0,197,2,140,127,3,3,196,128,175,65,13,196, +128,128,65,1,2,3,2,1,32,64,0,200,24,32,177,2,15,1,9,3,196,147,199,15,0,197,2,140,164,196,26,65, +1,27,13,32,170,2,16,1,15,3,196,147,221,14,0,204,110,65,13,32,161,2,21,1,19,3,196,17,196,73,196,128, +200,3,13,32,155,2,196,17,6,3,200,17,32,150,2,19,1,32,33,3,2,1,32,62,0,198,30,32,146,2,196,131, +133,14,3,196,148,22,13,0,198,45,32,142,2,13,1,32,47,3,3,1,32,42,0,21,1,5,3,3,1,4,3,2, +1,8,3,2,1,28,13,32,139,2,12,1,32,51,3,3,1,28,0,32,35,1,4,3,3,1,5,3,2,1,198,129, +42,4,13,32,137,2,196,130,94,32,37,3,3,1,23,0,32,40,1,4,3,3,1,198,34,198,128,157,65,13,32,134, +2,196,28,196,46,19,0,196,129,34,6,0,198,128,145,65,13,32,128,2,196,75,12,3,196,147,216,196,21,198,20,32, +124,2,196,128,133,23,3,196,147,231,196,129,79,19,0,196,129,60,198,93,65,3,4,1,28,13,32,121,2,196,128,145, +22,3,196,148,2,32,46,0,198,26,196,128,205,2,13,32,118,2,196,132,92,32,45,3,196,148,203,11,0,196,129,147, +200,129,150,4,13,32,114,2,11,1,32,79,3,2,1,32,59,0,198,22,32,109,2,196,59,13,3,196,148,238,10,0, +9,1,198,128,140,65,13,32,103,2,196,144,144,32,73,3,196,57,8,1,196,99,65,3,196,128,214,2,13,32,97,2, +196,131,44,32,70,3,196,129,4,6,1,198,129,164,4,13,32,91,2,196,130,5,32,82,3,200,20,196,145,9,196,148, +197,32,72,3,196,129,113,65,0,196,106,65,13,32,78,2,196,129,66,32,74,3,196,129,131,200,129,216,196,144,237,31, +13,196,145,119,196,140,54,32,82,3,196,129,151,198,129,22,196,129,237,196,129,149,4,13,32,68,2,196,92,30,3,196, +129,108,65,0,200,24,196,145,211,32,126,3,196,14,3,1,4,3,3,1,198,37,32,55,2,196,103,198,129,51,32,40, +0,2,1,197,1,129,79,196,53,65,13,196,146,47,196,113,32,36,3,196,129,224,9,1,196,129,99,196,21,196,146,95, +32,144,3,196,128,128,8,1,3,3,5,1,32,32,13,196,146,143,32,150,3,196,128,146,7,1,4,3,196,19,31,2, +196,128,174,32,62,3,196,128,163,3,0,3,1,196,54,65,13,26,2,196,20,5,3,198,18,198,50,65,13,22,2,196, +128,246,32,83,3,198,15,17,2,14,1,32,175,3,4,1,32,59,0,12,1,65,3,4,1,32,34,13,12,2,14,1, +32,181,3,3,1,32,41,0,32,34,1,32,35,13,196,132,112,32,184,3,198,129,230,12,1,32,35,13,3,2,196,33, +11,3,6,0,8,1,196,150,188,14,1,19,3,196,128,132,4,13,12,1,32,198,3,3,0,32,33,1,32,35,3,32, +39,13,8,1,32,165,3,3,1,32,37,3,21,1,32,47,3,32,38,13,65,1,4,1,32,169,3,3,1,32,104,3, +32,39,13,65,1,196,128,216,32,35,3,196,17,196,29,65,1,32,173,3,198,10,32,173,3,3,1,32,103,3,32,38, +13,3,1,196,14,198,131,140,32,86,3,196,14,32,174,3,2,1,3,3,8,1,32,92,3,32,37,13,4,1,196,18, +196,130,45,27,3,196,40,65,14,32,170,3,14,1,65,8,196,148,115,32,36,3,196,30,65,14,196,119,11,1,198,137, +203,32,82,3,32,36,13,4,1,2,14,32,163,3,11,1,12,8,196,148,116,32,34,3,196,35,65,14,32,86,3,198, +148,210,14,3,196,128,158,196,141,79,197,1,148,241,11,8,196,148,146,26,3,8,1,32,37,13,3,1,3,14,32,86, +3,2,1,196,149,84,12,3,196,150,195,3,3,13,1,5,8,196,140,191,32,55,3,32,33,1,198,87,65,14,196,33, +196,149,249,12,3,196,134,91,17,1,7,8,6,1,196,142,18,4,8,196,142,37,196,149,162,26,1,3,7,196,129,183, +2,13,196,124,2,14,196,73,3,3,10,1,3,8,2,1,12,3,196,133,202,15,1,12,8,6,1,65,8,6,1,198, +47,196,139,128,6,7,196,130,111,3,13,4,1,4,14,196,114,9,1,7,8,196,40,198,148,35,196,110,14,8,13,1, +196,80,3,3,196,143,64,196,132,128,65,1,5,7,196,41,3,1,5,14,32,84,3,196,142,206,196,77,196,137,45,196, +150,38,196,139,104,16,1,5,8,196,142,155,3,3,198,147,0,32,33,1,5,7,196,131,13,198,122,65,14,196,130,244, +196,28,2,8,196,119,5,1,2,3,17,1,22,8,17,1,6,8,196,42,198,132,207,17,1,19,7,196,131,50,2,13, +3,1,6,14,32,38,3,196,63,65,3,13,1,65,8,196,151,0,2,1,11,3,18,1,12,8,7,1,2,8,4,1, +4,8,198,67,21,3,198,133,1,196,139,224,2,7,7,1,2,7,4,1,5,7,7,1,26,13,4,1,6,14,32,38, +3,196,135,129,196,150,253,2,1,196,148,37,196,148,18,2,1,196,150,218,196,135,63,196,150,59,65,8,196,129,17,11, +1,8,8,196,128,155,3,1,196,61,196,141,230,2,1,3,7,3,1,65,7,7,1,65,7,196,139,112,25,13,196,127, +65,14,32,38,3,196,151,194,17,1,196,147,126,2,1,65,8,196,116,196,69,196,147,224,65,8,198,148,153,197,1,129, +114,196,128,192,21,3,196,136,215,65,7,2,1,2,7,3,1,2,7,3,1,204,71,65,1,23,13,196,128,130,65,14, +32,38,3,198,151,213,196,129,54,196,150,150,2,1,2,8,196,150,159,3,1,2,3,198,149,236,4,1,196,138,106,196, +138,82,196,21,196,148,192,196,140,67,196,74,196,141,54,2,1,65,7,198,69,196,5,198,128,137,196,133,233,65,7,196, +139,254,3,1,2,0,4,1,22,13,196,80,65,14,32,38,3,196,139,247,196,135,255,196,150,213,65,1,5,8,4,1, +3,8,200,80,198,11,5,1,198,129,13,196,148,99,196,21,65,8,196,137,51,65,3,3,1,196,139,126,202,78,6,1, +198,128,148,5,7,196,153,40,21,13,196,65,65,14,32,38,3,198,130,162,196,144,164,196,149,47,65,1,2,8,3,1, +65,8,6,1,3,8,3,1,2,3,196,150,16,198,82,3,1,196,148,101,66,1,8,3,1,198,149,42,5,8,208,77, +3,1,4,0,2,1,20,13,5,1,9,14,196,150,215,196,143,88,196,129,4,196,128,200,196,130,30,196,40,7,1,198, +60,198,143,97,196,141,0,196,151,118,65,8,196,129,102,197,1,151,150,196,129,42,196,16,200,128,135,65,1,200,129,17, +196,128,204,198,128,134,2,1,196,71,65,1,18,13,6,1,10,14,29,3,196,129,191,196,128,188,65,1,196,139,106,196, +144,210,198,72,198,151,179,197,1,151,183,200,128,144,198,128,145,65,1,200,130,170,196,128,199,196,129,0,8,1,3,7, +3,1,196,129,18,198,141,155,198,140,189,198,128,140,65,1,17,13,196,128,139,3,14,28,3,196,130,128,197,1,139,108, +196,141,124,2,1,2,8,200,129,92,198,128,199,198,150,24,198,130,229,198,128,216,5,1,196,145,43,196,129,14,196,129, +176,9,8,196,131,28,198,140,249,3,1,200,129,221,197,1,129,219,196,141,5,198,75,196,129,14,4,14,28,3,196,142, +4,65,8,196,68,196,129,150,65,1,196,10,2,8,2,1,65,8,2,1,200,9,2,1,196,79,2,1,8,8,196,128, +216,65,8,196,6,196,83,8,8,196,16,4,8,196,150,151,3,3,198,141,145,198,75,196,154,122,17,13,196,70,65,14, +28,3,198,37,9,8,198,150,145,196,142,5,196,76,15,1,196,145,116,3,3,196,138,247,196,139,32,196,150,86,196,62, +198,150,171,198,129,106,196,129,153,196,152,115,196,136,196,13,3,196,143,104,5,1,200,130,104,6,1,196,141,32,197,1, +129,241,196,140,158,65,1,16,13,196,78,65,14,28,3,2,1,196,152,213,196,128,213,196,150,226,212,85,2,1,196,145, +194,196,150,92,196,154,16,32,35,3,196,141,158,196,143,172,198,130,160,196,140,183,196,138,25,15,13,196,51,65,14,28, +3,14,1,196,140,177,65,1,196,129,197,197,1,152,194,196,128,135,196,139,154,196,139,119,196,130,237,4,3,196,139,127, +196,145,134,196,129,224,198,131,208,13,8,196,138,178,12,3,198,141,232,196,144,22,196,148,212,196,141,160,196,148,80,20, +13,196,71,65,14,28,3,8,1,196,130,6,196,74,200,130,21,196,129,155,7,8,196,129,208,197,1,151,136,5,1,5, +8,196,132,210,65,3,196,128,139,196,152,234,196,132,63,196,154,126,196,135,100,16,7,198,150,201,14,1,196,141,225,19, +13,196,129,212,7,14,28,3,196,44,65,8,3,1,196,152,8,198,130,86,2,8,196,129,151,196,151,84,196,130,78,196, +24,65,8,5,3,196,128,202,196,153,203,6,8,196,130,160,196,135,164,196,135,126,196,143,139,11,1,10,7,196,156,215, +7,13,196,129,209,7,14,28,3,2,1,65,8,2,1,6,8,3,1,196,67,9,1,198,130,225,196,129,122,196,131,159, +196,64,196,22,196,153,240,196,131,240,196,152,216,17,3,198,142,163,196,141,112,196,144,182,18,13,196,57,65,14,200,56, +198,129,181,65,8,196,129,113,196,131,210,196,129,9,196,64,196,151,201,196,130,90,196,152,61,196,132,34,196,153,243,9, +3,5,1,196,144,0,196,145,17,21,7,196,111,196,129,51,5,14,198,53,5,8,198,152,65,196,140,155,198,114,196,128, +148,196,131,153,196,141,213,198,151,69,11,1,32,56,3,196,141,237,25,7,14,1,18,13,196,129,94,6,14,196,44,197, +2,152,107,196,154,65,15,8,14,1,10,3,198,154,4,198,136,42,15,1,24,7,23,1,19,13,196,128,129,3,14,28, +3,196,154,112,2,1,196,142,25,65,8,196,131,220,198,131,225,196,138,240,196,155,192,16,3,32,32,1,32,59,3,15, +1,5,10,196,129,243,32,32,1,198,131,68,15,14,28,3,3,1,3,8,9,1,198,46,4,1,196,155,201,196,133,108, +196,154,164,16,3,16,1,10,10,32,42,1,24,13,196,85,2,14,196,37,196,154,216,2,1,196,81,196,132,46,196,130, +223,196,152,135,12,3,196,136,218,20,3,196,39,6,10,20,1,21,10,198,40,65,14,196,74,65,8,6,1,196,131,173, +13,8,196,134,99,196,157,4,4,1,32,61,10,23,13,196,129,143,10,14,196,30,196,131,23,196,131,76,65,1,196,156, +25,32,96,3,29,1,32,64,10,23,13,196,94,4,14,28,3,4,1,3,8,9,1,65,8,3,1,10,8,12,1,32, +93,3,27,1,32,72,10,22,13,6,1,30,14,29,3,3,1,196,126,196,146,248,196,156,253,32,62,3,21,1,32,82, +10,198,27,65,14,196,25,65,8,198,128,217,32,73,3,196,128,133,32,76,10,21,13,196,47,3,14,196,23,3,1,196, +153,80,196,133,156,32,54,3,17,1,32,97,10,20,13,7,1,32,34,14,196,48,196,132,238,196,134,234,196,46,9,10, +20,13,196,44,3,14,196,90,196,128,200,196,129,16,6,1,32,105,10,198,130,53,21,14,2,1,27,3,14,1,32,83, +3,19,1,32,111,10,18,13,7,1,32,39,14,7,1,22,3,7,1,32,88,3,17,1,32,90,10,25,1,16,13,7, +1,32,41,14,10,1,32,114,3,6,1,32,83,10,32,43,1,14,13,7,1,32,43,14,2,4,12,1,32,108,3,15, +1,32,70,10,32,49,1,5,13,4,1,3,13,7,1,32,45,14,7,4,196,137,77,20,3,27,1,32,57,10,18,1, +32,32,10,14,1,65,13,196,27,2,14,10,4,196,135,142,15,1,3,9,28,1,196,23,9,1,2,11,65,1,32,35, +10,20,1,32,49,14,14,4,196,137,129,4,3,14,1,15,9,32,48,1,4,10,16,1,10,11,65,1,32,33,10,196, +31,2,14,18,4,196,137,180,196,28,12,9,32,54,1,17,11,65,1,196,84,196,129,75,24,14,22,4,10,1,32,66, +3,15,1,32,46,9,32,34,1,22,11,2,1,31,10,19,1,32,53,14,24,4,196,130,77,196,157,136,32,53,9,24, +11,8,1,24,11,198,25,28,4,13,1,32,45,3,17,1,32,58,9,32,57,11,65,1,28,10,196,92,2,14,32,32, +4,13,1,196,138,211,18,1,32,63,9,32,58,11,3,1,25,10,196,25,32,35,4,14,1,21,3,22,1,32,69,9, +32,60,11,65,1,24,10,21,1,32,53,14,32,40,4,15,1,8,3,196,25,7,9,202,24,32,44,4,32,34,1,32, +83,9,32,61,11,32,45,1,32,53,14,32,49,4,20,1,32,92,9,32,64,11,65,1,21,10,196,97,32,55,4,10, +1,4,4,202,20,32,60,4,5,1,3,4,32,93,9,198,29,196,128,163,65,14,32,68,4,32,93,9,32,65,11,65, +1,20,10,196,128,138,204,17,204,19,32,67,4,32,94,9,32,66,11,21,10,196,128,199,202,15,196,17,32,65,11,8, +10,13,1,65,10,17,1,32,55,14,198,17,15,10,16,1,32,55,14,196,10,2,10,14,1,32,55,14,196,51,198,108, +4,10,65,1,32,68,14,196,62,196,119,20,10,8,1,32,66,14,196,12,5,10,6,1,9,10,196,14,32,66,4,32, +95,9,32,61,11,9,1,13,10,10,1,32,66,14,196,19,196,128,209,2,1,196,43,3,1,32,65,14,196,15,196,128, +169,11,1,32,65,14,196,43,32,59,11,2,1,22,10,196,15,196,12,65,1,4,10,11,1,8,10,196,27,196,24,196, +76,5,1,196,11,196,74,32,55,11,2,1,8,10,196,5,196,24,198,14,196,95,11,10,196,80,32,65,4,32,95,9, +196,26,196,44,9,10,9,1,32,66,14,196,18,32,54,11,17,1,2,10,11,1,2,10,196,128,140,196,34,32,53,11, +2,1,9,10,7,1,198,21,3,10,196,130,137,29,14,196,54,32,51,11,196,129,103,5,1,6,10,196,130,154,28,14, +196,71,32,49,11,196,129,120,13,10,2,13,196,130,171,26,14,196,88,32,47,11,4,1,32,39,10,4,13,196,129,233, +8,14,196,105,65,9,32,38,11,3,1,3,11,196,55,32,34,10,5,13,196,130,0,7,14,196,24,32,35,11,14,1, +196,91,23,10,5,13,196,130,229,22,14,196,42,32,33,11,32,44,1,8,10,3,1,2,10,6,13,196,130,250,21,14, +196,63,31,11,8,1,196,148,37,32,33,1,7,13,196,131,13,19,14,196,82,30,11,6,1,10,3,196,156,157,196,136, +247,3,1,9,13,196,131,36,17,14,196,105,27,11,196,158,165,7,3,10,13,196,131,52,16,14,196,121,25,11,196,132, +195,65,3,11,13,196,131,187,22,14,196,128,137,23,11,196,18,3,3,196,17,21,14,196,128,150,22,11,196,132,61,12, +13,196,131,215,20,14,196,128,165,20,11,196,132,76,3,3,196,18,19,14,196,128,179,65,9,17,11,196,17,2,3,13, +13,196,131,247,18,14,196,19,15,11,196,60,8,3,14,13,196,132,6,17,14,196,34,14,11,196,47,6,3,196,16,16, +14,196,129,166,2,9,11,11,196,62,196,31,65,13,196,134,84,29,14,196,19,10,11,196,77,2,3,8,1,198,134,101, +28,14,196,34,8,11,196,92,196,160,221,16,13,196,132,67,13,14,196,50,7,11,196,108,196,161,29,198,135,140,29,14, +196,64,134,62,2,11,196,124,15,3,198,133,147,20,14,196,16,65,11,196,160,41,65,3,196,141,111,6,3,198,133,166, +19,14,196,35,198,148,137,196,141,155,2,3,198,133,53,15,14,196,130,104,3,9,198,161,49,20,1,29,3,196,133,106, +196,132,150,8,14,196,130,124,2,9,196,160,18,196,137,229,198,161,119,6,1,20,13,196,134,221,20,14,196,130,147,65, +9,196,158,207,196,142,129,196,144,215,196,131,160,198,136,198,26,14,196,23,196,139,69,4,3,23,13,196,135,1,18,14, +196,130,183,198,161,122,196,159,168,25,3,24,13,196,135,19,17,14,196,130,201,198,142,180,196,143,10,32,56,3,25,13, +196,135,38,16,14,198,20,32,41,3,17,1,24,13,196,135,52,15,14,196,130,234,196,153,137,196,159,252,196,161,43,196, +137,169,196,138,138,25,14,196,21,32,58,3,196,35,65,13,196,135,86,14,14,196,15,16,3,27,13,196,135,97,13,14, +196,12,65,3,27,13,3,1,29,14,200,11,65,1,28,14,198,17,65,13,196,135,122,11,14,196,131,48,196,138,150,32, +48,3,19,1,25,13,196,135,140,11,14,32,67,4,32,75,9,8,1,11,9,198,26,196,134,219,200,139,52,22,14,32, +68,4,32,69,9,65,3,25,1,32,82,3,2,1,13,7,7,1,24,13,3,1,26,14,32,68,4,32,67,9,3,3, +25,1,8,3,7,1,32,67,3,3,1,16,7,3,1,24,13,4,1,25,14,32,68,4,32,64,9,4,1,32,35,3, +28,1,32,46,3,3,1,17,7,2,1,25,13,4,1,24,14,32,68,4,32,62,9,196,161,63,196,130,61,196,145,63, +11,3,200,30,65,13,3,1,24,14,32,68,4,32,60,9,196,159,92,17,3,32,45,1,15,3,198,25,65,1,23,14, +32,68,4,32,58,9,196,99,196,161,148,19,1,198,71,196,139,119,17,14,32,68,4,32,56,9,7,1,32,89,3,27, +1,18,7,2,1,27,13,4,1,22,14,32,68,4,32,55,9,6,1,32,116,3,2,1,18,7,2,1,28,13,4,1, +21,14,32,68,4,32,53,9,196,24,2,3,200,23,196,107,21,14,32,68,4,32,51,9,196,17,2,3,200,128,180,196, +133,121,20,14,32,68,4,32,49,9,7,1,32,121,3,22,1,30,13,3,1,20,14,32,68,4,32,48,9,196,38,196, +140,149,11,1,196,73,19,14,32,69,4,32,46,9,5,1,32,127,3,9,1,11,7,2,1,29,13,3,1,19,14,32, +69,4,32,44,9,196,78,10,3,196,149,141,3,7,2,1,196,146,36,198,138,172,9,14,32,69,4,32,41,9,196,141, +75,32,39,3,198,28,2,13,8,1,19,13,196,136,242,2,14,32,69,4,32,39,9,196,26,2,3,198,51,65,13,12, +1,198,137,136,3,14,32,69,4,32,36,9,196,22,3,3,198,139,139,6,1,2,12,6,1,198,137,83,2,14,32,69, +4,32,35,9,196,128,155,17,3,198,139,164,4,1,6,12,5,1,198,137,108,65,14,32,69,4,32,33,9,196,26,2, +3,198,139,188,3,1,9,12,3,1,16,13,65,14,3,1,16,14,32,69,4,32,32,9,196,128,148,12,3,196,148,174, +5,1,10,12,3,1,65,12,198,137,162,32,69,4,31,9,196,128,195,18,3,196,149,152,196,77,10,12,15,13,196,51, +15,14,32,69,4,29,9,196,24,196,144,230,10,7,7,1,17,12,196,137,204,32,69,4,28,9,5,1,32,148,3,17, +1,19,12,17,13,3,1,14,14,32,70,4,25,9,196,87,196,164,154,2,1,198,21,134,18,11,14,32,70,4,23,9, +7,1,32,161,3,5,1,20,12,18,13,3,1,13,14,32,70,4,22,9,196,125,20,3,196,121,9,12,3,11,196,60, +134,59,10,14,32,70,4,21,9,196,80,16,3,3,1,21,12,3,11,196,43,12,14,32,70,4,19,9,196,41,3,3, +3,1,20,12,5,11,3,1,14,13,3,1,12,14,32,70,4,16,9,196,84,6,3,202,24,196,130,22,11,14,196,17, +6,1,196,163,142,196,129,97,196,43,19,12,6,11,4,1,196,41,11,14,32,71,4,14,9,32,93,1,32,81,3,4, +1,3,5,16,12,7,11,198,66,65,1,10,14,196,26,21,1,196,135,7,32,76,1,32,58,3,4,1,4,5,13,12, +3,5,8,11,198,95,10,14,32,71,4,13,9,196,163,48,196,135,38,32,56,1,32,43,3,3,1,4,5,11,12,3, +1,198,34,65,1,9,14,196,32,196,142,184,196,141,105,196,131,215,13,3,196,59,9,12,5,1,2,5,9,11,196,128, +153,196,130,158,9,14,32,72,4,11,9,4,1,32,113,3,15,1,32,42,3,3,1,6,5,65,12,2,1,2,12,8, +1,2,5,10,11,2,1,15,13,3,1,9,14,196,40,196,143,117,196,131,13,198,31,9,1,196,122,2,11,198,25,65, +1,8,14,196,23,32,67,3,2,1,8,5,196,46,4,5,198,46,196,130,239,8,14,32,73,4,9,9,3,1,32,172, +3,3,1,21,5,10,11,65,5,17,13,3,1,8,14,198,25,32,171,3,4,1,24,5,6,11,2,5,18,13,3,1, +7,14,32,45,4,196,134,254,20,4,8,9,196,143,215,65,3,5,1,26,5,2,11,5,5,17,13,196,30,65,1,32, +42,4,14,1,16,4,8,9,196,143,238,32,66,3,4,1,4,11,31,5,196,28,3,1,32,38,4,20,1,13,4,6, +9,198,144,12,2,1,5,11,31,5,16,13,196,142,83,6,1,32,35,4,198,157,106,10,1,10,4,5,9,198,144,40, +65,1,7,11,32,32,5,16,13,196,142,170,8,1,32,32,4,196,152,163,9,1,8,4,200,30,134,101,29,5,196,24, +196,166,233,28,4,196,152,115,65,4,7,1,6,4,4,9,198,128,133,196,129,176,30,5,2,1,14,13,196,143,82,65, +14,5,0,8,1,24,4,196,159,85,5,6,7,1,5,4,3,9,196,128,139,65,3,2,11,3,1,198,41,65,1,14, +13,196,143,82,196,167,187,196,135,192,17,4,196,38,3,6,7,1,3,4,2,9,196,128,203,198,37,196,129,248,4,5, +6,1,20,5,198,129,255,5,14,196,146,44,16,4,196,152,23,7,6,8,1,65,9,196,128,236,2,3,10,11,3,1, +3,5,7,1,3,11,17,5,196,129,139,4,14,12,0,196,128,156,6,4,196,159,198,196,39,196,144,104,196,166,21,196, +166,230,32,35,3,196,128,248,3,11,196,45,6,1,5,11,16,5,196,43,15,0,196,128,224,196,159,174,10,6,8,1, +19,3,196,138,163,196,134,168,196,165,76,4,3,5,1,2,11,3,1,65,11,196,49,8,11,16,5,196,130,53,3,14, +18,0,11,1,6,4,196,46,196,156,53,20,3,196,133,246,196,137,70,27,1,32,38,3,4,1,3,11,197,1,45,12, +11,6,1,13,5,196,130,8,3,14,21,0,13,1,65,4,196,152,60,14,6,196,157,14,10,1,196,166,78,196,151,103, +25,3,196,158,65,25,1,32,32,3,4,1,65,7,7,11,196,163,34,65,11,3,7,196,130,172,196,163,44,11,5,196, +130,66,65,1,2,14,25,0,196,159,156,9,6,4,1,196,156,43,196,151,182,196,141,74,16,3,196,159,219,65,7,196, +150,23,12,3,4,1,3,7,7,11,9,7,4,11,5,1,7,7,9,5,198,54,29,0,196,160,93,5,6,196,49,5, +1,7,3,198,155,66,196,150,82,18,3,196,149,148,2,7,196,152,81,4,1,4,7,196,48,2,7,196,46,65,7,9, +5,196,131,250,3,1,2,14,65,1,32,32,0,196,129,98,196,159,228,5,6,196,155,91,196,154,58,196,155,198,196,168, +5,198,55,196,143,138,6,11,12,7,196,155,144,200,49,66,1,14,196,169,157,22,0,198,158,113,12,6,4,1,6,6, +196,144,143,15,1,196,152,132,196,149,122,8,3,196,159,12,196,160,16,7,7,198,102,17,7,196,156,150,6,5,6,1, +17,13,3,1,65,14,196,166,221,17,0,198,158,165,11,6,196,158,164,196,160,127,196,167,61,196,144,174,198,134,17,24, +1,14,3,196,149,210,196,157,88,196,144,132,196,153,29,198,143,190,21,7,196,155,69,5,5,7,1,17,13,4,1,196, +130,56,196,169,62,16,0,196,161,242,13,6,196,113,196,159,89,198,144,172,9,7,5,1,196,164,72,10,1,6,3,196, +164,81,196,161,246,6,7,198,144,121,65,1,198,144,253,8,7,196,144,0,196,164,197,196,70,196,130,28,65,1,2,7, +17,13,3,1,4,4,196,167,93,16,0,9,1,15,6,196,159,35,196,151,170,2,3,198,143,95,32,34,7,14,1,196, +149,220,198,144,244,198,157,138,196,144,120,196,155,15,196,62,32,35,7,4,5,196,156,40,17,13,3,1,6,4,196,167, +149,15,0,10,1,198,128,236,196,152,25,196,157,253,198,144,224,196,152,144,30,7,196,168,81,3,1,3,7,2,1,196, +157,239,196,155,0,65,7,202,58,65,13,2,1,9,4,7,1,25,0,10,1,198,159,139,196,152,73,196,156,110,198,145, +16,196,161,31,196,141,127,196,142,230,2,1,198,50,196,152,205,7,7,196,144,162,2,3,196,129,191,4,5,196,156,227, +16,13,2,1,10,4,196,168,75,196,169,134,198,162,186,5,6,196,153,7,198,58,2,1,4,7,196,134,133,196,156,143, +3,7,198,156,73,198,158,119,8,7,196,165,241,4,3,196,129,246,3,5,196,129,186,65,7,15,13,2,1,13,4,198, +128,162,5,6,196,159,93,196,153,135,200,145,186,196,163,33,198,157,127,196,144,244,4,7,196,155,220,65,7,196,142,189, +5,1,196,158,182,196,134,208,196,128,130,12,1,15,3,196,130,54,2,5,196,65,2,7,14,13,2,1,15,4,196,121, +7,1,197,1,163,76,196,153,124,198,145,249,196,130,18,197,1,165,162,196,145,119,198,156,187,11,7,196,168,25,196,158, +163,65,1,196,145,215,196,155,198,196,129,114,196,166,35,196,128,186,196,161,209,196,127,65,1,18,4,196,62,196,148,85, +196,166,49,196,155,193,8,7,200,146,60,196,145,108,196,159,113,6,7,196,151,64,196,159,129,4,7,2,1,196,154,161, +196,163,148,196,141,159,196,160,203,196,146,87,196,159,62,196,163,244,196,62,20,4,196,169,141,12,0,196,129,253,196,144, +79,65,1,200,62,5,7,3,1,5,3,3,1,12,7,198,53,20,3,18,1,16,7,13,1,3,7,3,12,196,146,88, +14,13,65,1,23,4,196,169,131,196,170,123,198,170,171,196,156,155,196,129,246,196,136,72,200,146,110,196,146,31,198,158, +215,200,59,2,1,196,129,88,196,146,124,29,3,196,156,239,196,152,9,65,7,5,12,196,152,71,13,13,65,1,27,4, +196,169,189,2,0,196,170,112,196,131,10,196,134,46,196,154,77,14,1,198,146,229,198,158,45,196,146,231,196,160,39,196, +146,179,196,162,180,196,159,215,198,68,6,3,196,164,94,196,135,36,2,12,3,1,7,12,18,7,196,136,87,2,1,8, +13,30,4,14,1,32,34,0,13,1,4,3,196,147,39,198,147,94,5,1,3,7,3,1,2,7,198,145,162,6,7,196, +155,239,196,160,57,196,161,170,196,129,214,65,3,196,164,159,196,135,77,10,12,17,7,9,1,6,13,30,4,196,160,68, +198,62,196,144,106,198,165,35,196,55,197,1,164,159,196,159,209,196,156,2,198,150,71,196,157,23,16,7,196,136,226,196, +156,16,23,12,198,48,196,44,5,6,14,1,32,32,0,14,1,4,7,7,1,7,7,3,1,9,7,3,1,2,7,3, +1,6,7,196,129,204,65,7,3,1,4,3,197,1,15,196,157,209,196,107,65,3,196,20,23,12,19,1,4,7,3,1, +6,13,196,61,196,160,64,65,1,31,0,13,1,65,7,198,160,157,2,7,198,147,59,8,7,2,1,5,3,196,50,196, +130,109,4,1,4,7,4,11,3,1,4,7,20,12,22,1,7,7,6,13,30,4,196,160,117,196,129,132,196,151,95,6, +1,2,7,9,1,200,109,196,130,154,3,1,4,3,4,1,65,7,196,160,15,196,161,241,65,7,3,1,3,3,65,8, +196,155,80,65,3,5,1,8,11,4,1,3,7,18,12,10,1,5,5,196,160,74,6,13,30,4,196,164,231,196,133,180, +196,132,15,196,171,80,10,1,196,157,198,196,163,202,198,128,169,198,167,227,197,1,165,137,196,162,1,198,66,204,64,65, +1,196,168,115,10,12,6,1,9,5,196,165,239,6,13,31,4,196,158,62,197,1,160,101,196,171,154,196,173,4,15,1, +196,157,254,10,1,10,7,198,151,90,196,158,8,196,165,193,197,1,159,200,196,130,63,196,148,75,65,3,196,148,49,198, +155,207,11,11,196,136,109,3,12,196,135,144,7,5,196,128,154,6,13,32,32,4,196,165,99,197,1,166,27,196,171,223, +196,134,64,10,1,196,160,34,198,161,156,3,7,198,151,160,6,7,3,1,196,158,121,196,162,128,6,1,2,8,196,156, +14,3,1,13,11,196,136,226,8,12,196,66,2,5,196,162,151,7,13,32,33,4,198,163,48,4,6,3,1,2,6,196, +167,73,196,70,196,166,137,196,161,70,196,162,53,2,7,196,153,252,196,161,226,5,7,198,138,172,198,63,4,11,3,1, +7,11,2,1,13,12,9,1,12,5,196,162,128,65,1,7,13,32,33,4,13,1,8,6,3,1,2,6,3,1,196,168, +56,196,133,58,3,0,196,159,36,65,7,198,149,6,5,3,196,138,59,196,166,191,65,7,6,1,3,8,196,127,198,67, +2,11,196,128,255,8,1,12,5,11,7,3,1,7,13,32,34,4,16,1,196,162,226,196,165,16,10,2,15,1,196,130, +90,196,148,232,196,138,111,5,3,196,159,39,196,166,222,5,1,196,147,66,22,3,200,134,106,8,11,8,12,198,148,2, +196,135,249,2,5,198,160,157,7,13,196,142,255,65,1,196,57,196,168,171,196,133,19,8,0,19,1,16,7,6,3,65, +1,15,7,196,132,130,65,7,4,1,5,8,198,156,252,65,1,15,11,6,12,198,167,147,65,1,9,5,4,1,9,7, +3,1,8,13,32,46,4,196,168,85,196,156,68,196,132,159,196,174,176,196,133,53,7,3,15,7,196,162,89,196,170,35, +198,129,227,196,169,244,15,11,5,12,196,132,87,196,167,65,196,133,181,196,163,62,196,167,106,8,13,32,49,4,196,169, +180,196,157,234,196,175,86,24,0,28,1,2,3,16,7,196,159,176,198,109,196,129,89,4,3,16,11,5,12,196,162,122, +196,137,66,196,157,239,196,160,235,8,13,32,48,4,196,169,228,196,168,157,196,128,217,196,129,18,3,7,196,167,225,198, +128,154,198,149,95,28,3,196,98,198,163,63,196,99,196,129,66,2,13,32,47,4,5,1,2,2,196,156,206,196,172,50, +196,174,208,2,1,198,129,127,3,7,5,1,198,157,41,9,3,3,11,3,1,8,11,5,1,65,12,11,7,14,1,11, +7,5,1,10,13,32,46,4,196,156,230,196,153,5,196,153,113,29,1,12,7,196,170,133,196,130,153,4,3,196,45,196, +136,41,196,130,64,198,168,51,196,128,131,3,13,32,45,4,196,156,208,196,168,82,196,175,199,196,160,221,196,170,172,196, +130,194,9,1,196,136,5,196,162,254,196,165,79,5,7,196,131,211,4,13,32,43,4,196,170,209,196,40,6,1,196,163, +131,11,8,198,165,174,196,136,41,196,129,0,196,132,239,24,7,13,13,32,42,4,196,157,22,16,2,196,174,104,196,175, +167,196,168,255,196,165,202,6,8,196,157,196,196,137,129,65,5,198,150,233,198,150,152,196,128,222,196,134,135,196,128,208, +2,13,32,40,4,196,169,228,32,70,2,196,132,108,9,0,196,166,239,196,152,13,65,1,12,8,196,167,102,196,162,127, +10,5,196,164,18,4,7,198,134,169,17,7,8,1,9,13,32,38,4,8,1,32,81,2,12,1,29,0,5,1,12,7, +7,1,5,0,6,1,13,8,3,1,19,3,5,1,12,5,5,7,4,1,2,7,3,1,3,7,2,1,29,7,10,1, +7,13,32,38,4,196,171,127,26,2,15,1,22,0,198,164,92,7,1,4,0,198,165,218,19,3,3,1,14,5,11,7, +200,43,5,7,5,1,7,13,32,37,4,196,39,196,164,65,196,176,108,3,0,196,164,18,12,1,3,0,198,171,151,196, +136,1,4,3,18,5,10,7,196,41,2,7,196,130,130,32,36,4,196,78,196,130,123,6,1,6,0,196,161,158,8,1, +2,0,198,171,189,14,1,3,3,20,5,13,7,196,157,182,65,5,6,13,32,35,4,5,1,32,108,2,22,1,7,7, +10,1,2,7,4,1,65,0,5,1,16,8,3,1,65,0,26,1,13,5,13,7,3,1,7,7,6,1,22,7,8,1, +3,13,32,33,4,196,128,161,27,2,16,1,196,149,203,196,163,40,196,173,4,198,177,122,22,1,6,5,196,41,196,161, +220,200,165,92,196,152,82,4,1,2,13,32,32,4,196,88,15,2,9,1,196,164,213,196,162,215,196,173,98,196,151,105, +17,0,19,1,4,5,198,163,43,4,7,196,162,235,196,163,122,196,165,188,7,1,196,133,133,2,13,31,4,196,48,7, +2,196,151,203,196,136,215,196,129,132,7,8,196,156,190,196,176,6,198,47,196,135,120,4,1,198,166,5,196,165,65,5, +12,3,1,2,13,29,4,196,128,131,196,171,137,2,1,9,7,196,43,196,87,20,0,3,5,196,134,196,6,7,4,1, +3,7,5,1,65,12,2,1,7,7,3,1,6,12,3,1,2,13,28,4,196,128,176,19,2,10,1,196,45,196,167,31, +32,37,0,196,168,166,198,133,213,196,133,248,6,7,198,139,150,198,83,27,4,196,121,5,2,196,168,217,196,166,104,22, +8,196,157,51,9,0,196,167,229,200,153,25,196,166,120,65,1,196,170,184,12,12,3,13,25,4,196,71,196,170,240,196, +134,234,196,153,48,23,8,196,135,249,8,0,196,141,125,7,1,196,172,191,65,1,196,35,23,4,196,103,5,2,198,129, +227,196,175,173,14,1,26,0,196,132,146,12,1,3,12,13,1,196,62,21,4,7,1,32,140,2,5,1,10,7,8,1, +24,8,23,1,17,0,9,1,5,7,8,1,8,12,5,1,4,12,2,1,20,12,4,13,19,4,196,38,3,2,5,1, +196,163,96,24,8,3,1,6,0,29,1,4,0,9,1,3,7,6,1,13,12,196,141,9,4,12,5,13,18,4,196,37, +2,2,19,1,25,8,3,1,13,0,22,1,5,0,7,1,3,5,196,73,4,12,196,141,23,7,1,3,13,17,4,196, +128,134,10,2,13,1,65,2,196,166,16,196,152,232,196,138,175,6,1,4,0,10,5,4,1,65,12,196,5,3,12,3, +1,196,129,67,196,133,216,2,13,16,4,196,128,247,17,2,8,1,3,2,196,166,60,196,169,59,32,39,0,9,5,196, +41,204,41,66,1,13,15,4,196,173,110,32,82,2,196,166,86,5,8,196,129,19,2,0,9,5,3,1,4,12,3,1, +4,12,196,68,18,12,2,1,6,12,3,1,65,13,15,4,3,1,196,162,27,196,178,92,32,87,2,200,46,65,0,3, +5,3,1,2,5,197,1,135,23,7,12,196,34,196,32,196,166,227,32,142,2,196,72,196,129,165,3,0,202,30,2,12, +2,13,196,53,196,133,11,196,160,227,20,2,196,95,196,129,146,3,0,198,53,196,141,214,196,129,7,8,12,2,13,196, +82,16,2,196,173,45,32,115,2,196,124,3,8,196,129,141,65,0,3,1,7,5,196,135,103,3,1,4,12,196,142,38, +2,13,196,36,196,172,67,196,131,85,32,39,2,196,37,196,129,253,196,141,185,196,119,196,130,25,11,12,198,128,163,196, +142,220,5,12,2,1,65,12,3,13,196,76,11,2,196,166,253,32,46,2,196,173,185,6,2,196,170,35,196,82,196,128, +158,2,1,4,5,196,129,7,65,1,10,12,198,128,211,196,136,1,196,128,215,4,13,196,46,196,161,157,32,64,2,196, +174,181,2,2,196,124,196,130,42,3,1,65,5,3,1,198,45,9,12,200,136,37,198,130,120,2,13,196,82,14,2,196, +167,116,32,35,2,196,173,207,7,2,196,128,162,196,128,163,2,1,2,5,3,1,5,5,3,1,4,12,4,1,197,1, +143,90,11,12,4,1,4,13,196,46,196,162,12,196,174,12,32,42,2,196,168,69,196,178,216,196,177,29,196,130,170,196, +128,206,196,49,6,12,196,129,65,2,12,195,1,126,2,12,196,45,65,13,196,90,16,2,196,174,63,22,2,198,168,115, +7,1,10,2,196,128,252,4,8,196,130,137,4,1,8,5,196,128,210,65,12,4,1,9,12,3,1,10,12,5,1,5, +13,196,46,196,162,101,65,1,32,37,2,196,171,110,2,7,10,2,196,177,118,196,130,217,65,0,5,1,6,5,196,42, +4,12,5,1,197,1,143,224,2,1,6,13,196,89,14,2,14,1,32,33,2,196,43,9,2,196,86,196,131,41,196,179, +85,5,5,196,134,226,198,143,231,5,1,3,12,196,132,74,16,4,196,175,240,32,47,2,14,1,27,2,200,173,222,8, +7,3,1,8,2,196,128,130,196,129,155,9,1,198,142,243,4,1,8,12,10,1,65,12,5,1,8,13,198,45,196,164, +42,196,174,90,196,155,110,196,173,153,3,1,196,174,152,196,178,155,13,8,196,160,131,7,1,5,5,196,131,157,9,1, +7,12,16,1,9,13,198,87,196,128,166,65,1,15,2,196,156,158,65,7,196,137,67,198,156,168,196,49,32,37,8,196, +160,174,5,0,2,1,196,140,43,198,141,93,196,144,159,11,1,10,13,198,128,132,196,158,219,5,1,9,2,196,168,3, +197,1,155,203,6,2,196,164,177,196,160,216,4,0,196,143,58,10,1,3,11,198,131,204,65,1,196,143,247,196,133,129, +2,13,198,128,179,20,2,15,1,5,2,196,168,234,198,156,132,6,1,196,156,135,196,51,196,130,98,6,0,196,130,44, +5,1,7,11,12,1,196,143,251,6,13,7,1,196,44,5,2,11,1,3,2,197,1,174,184,197,1,174,131,196,156,177, +196,174,84,5,2,196,172,47,11,8,196,135,87,13,0,196,143,155,198,136,238,9,1,196,141,76,198,157,244,5,1,196, +51,6,2,4,1,4,2,196,156,81,5,1,200,133,153,2,1,196,50,196,165,62,196,95,65,0,196,144,33,65,5,4, +1,65,11,5,1,196,141,122,9,1,196,10,198,158,38,2,1,65,11,3,1,196,55,8,2,200,51,196,174,143,5,2, +196,165,146,198,128,229,196,130,102,196,43,196,43,9,11,14,1,198,158,75,65,1,4,11,2,1,196,92,196,175,200,14, +7,196,140,195,196,157,56,5,2,196,166,39,196,181,223,2,0,196,85,196,149,107,196,137,114,196,73,196,142,188,8,1, +6,11,2,1,196,128,134,7,2,196,173,76,196,82,196,38,196,181,7,2,1,196,136,88,196,104,196,141,36,196,128,222, +2,1,196,128,164,196,128,165,196,132,157,196,132,29,196,139,74,6,2,196,75,9,1,10,11,3,1,196,143,33,10,5, +198,136,195,196,128,197,6,2,196,157,93,196,157,230,65,1,4,7,3,1,6,2,196,166,148,198,129,123,3,1,65,7, +196,142,252,3,11,3,1,196,104,14,5,196,7,65,1,196,130,36,32,41,2,196,169,1,197,2,177,171,196,128,153,65, +2,196,44,198,157,217,11,11,198,36,196,150,2,196,128,237,14,4,196,177,229,32,49,2,196,157,159,196,139,10,200,157, +242,202,82,65,1,196,174,95,198,35,65,11,4,1,65,13,12,4,196,134,174,24,2,196,129,233,198,158,137,198,112,198, +129,2,198,117,20,11,196,131,181,6,5,3,1,4,11,3,1,2,13,11,4,4,1,65,2,196,164,255,8,2,196,171, +117,198,169,148,196,155,140,196,129,1,198,130,12,196,128,160,196,144,109,198,143,101,198,143,187,10,5,196,49,5,13,10, +4,5,1,196,45,7,2,4,1,196,137,226,6,7,200,128,189,65,1,2,7,4,1,6,11,5,1,196,38,3,1,2, +11,7,13,9,4,5,1,2,2,196,165,79,196,129,172,196,136,75,3,7,196,128,227,196,166,189,3,8,196,136,185,14, +0,3,1,65,7,5,1,196,49,10,11,196,137,76,4,5,196,150,112,9,4,196,128,196,2,2,196,169,230,198,176,191, +196,171,255,3,1,198,129,127,65,0,196,136,131,196,87,9,11,198,128,172,196,136,167,8,4,196,135,3,5,2,196,173, +182,10,1,3,7,206,35,10,13,8,4,196,55,196,142,103,11,7,196,143,101,3,1,196,129,218,196,183,141,65,0,7, +7,2,1,196,146,88,196,128,140,196,136,220,7,4,196,90,65,2,3,1,196,140,236,2,7,5,1,6,7,198,91,7, +7,196,138,129,196,129,138,65,5,7,1,5,13,6,4,198,34,66,1,7,11,1,8,7,4,1,4,7,4,1,198,129, +248,6,1,2,7,196,37,9,1,4,13,5,4,196,117,196,183,9,200,159,70,200,139,255,196,129,125,198,128,197,13,0, +8,1,196,159,86,17,11,196,138,190,196,130,66,3,13,4,4,196,128,153,3,2,197,1,179,73,197,1,159,236,196,31, +196,130,99,196,131,27,196,171,33,196,138,233,196,137,60,196,137,248,196,132,125,198,130,98,196,46,3,4,196,128,143,196, +183,89,198,129,50,196,131,116,12,1,198,130,103,196,164,28,196,162,104,196,131,133,196,139,22,196,129,176,14,5,196,145, +241,196,130,177,3,13,2,4,196,128,187,3,2,196,159,130,196,170,252,5,1,196,129,250,196,168,182,196,164,74,196,159, +123,65,7,3,1,2,7,196,130,5,2,11,10,1,9,5,198,139,19,3,13,65,4,196,48,196,169,18,198,172,85,9, +7,196,129,13,196,133,206,196,162,191,196,142,42,196,177,161,196,177,132,10,11,13,1,8,5,196,145,85,196,128,135,196, +41,196,156,124,196,131,10,196,169,5,196,133,244,196,80,196,173,112,196,130,117,65,11,14,1,65,11,6,5,196,145,121, +65,11,2,1,3,13,196,80,196,134,218,7,7,5,1,2,7,196,171,198,3,1,4,2,196,168,202,65,8,196,138,122, +9,0,196,172,8,198,171,76,7,11,8,1,4,11,2,1,196,146,188,196,138,33,3,1,3,13,196,167,214,31,2,196, +130,213,196,143,252,197,1,160,124,198,131,70,196,48,196,185,241,198,173,202,196,131,108,10,1,5,11,6,5,9,11,196, +155,23,196,167,62,198,131,193,196,132,27,198,130,254,5,2,196,168,214,2,8,196,136,255,32,37,0,196,107,196,174,113, +66,1,11,2,5,20,1,196,185,14,32,59,2,196,131,35,198,128,240,198,160,201,196,173,192,4,2,198,169,38,196,165, +77,198,129,229,196,140,27,196,161,21,9,6,9,1,196,129,200,13,1,32,122,2,196,143,217,196,138,178,196,171,225,196, +135,46,16,8,196,80,196,130,10,13,7,196,174,135,196,174,105,12,1,32,122,2,198,161,8,197,2,178,142,196,172,75, +4,2,196,133,191,196,171,20,32,46,0,19,7,196,178,26,5,6,196,137,124,12,1,32,122,2,197,1,172,34,198,174, +202,9,7,196,128,240,196,169,108,196,130,174,196,130,80,11,7,196,170,218,196,177,104,198,184,170,4,1,32,121,2,198, +172,80,196,142,170,4,1,196,135,150,14,8,196,130,209,2,0,196,130,94,196,178,73,2,6,6,1,14,0,65,1,32, +121,2,198,161,178,196,172,110,196,144,51,196,129,56,196,183,114,196,182,46,32,49,0,12,1,7,7,196,40,196,177,177, +15,0,32,121,2,14,1,196,132,235,198,173,1,4,2,196,183,147,65,8,196,183,232,8,1,3,12,2,1,196,175,82, +196,146,145,196,187,53,5,0,196,168,100,2,2,196,133,61,200,138,127,10,7,3,1,3,2,198,43,6,1,196,135,226, +3,12,196,143,158,30,6,196,187,89,4,0,196,138,186,31,2,197,2,167,109,5,7,196,172,59,196,112,196,184,48,196, +136,178,198,136,74,198,161,251,31,6,196,136,133,196,135,9,32,82,2,198,162,67,196,140,152,196,143,213,4,2,198,184, +36,196,133,88,196,138,63,6,12,198,136,111,65,12,196,128,187,6,6,196,136,174,196,180,42,196,134,138,32,64,2,198, +138,238,66,1,7,198,161,236,4,7,3,1,4,2,196,184,79,7,8,196,88,3,12,202,47,196,164,222,196,169,184,65, +2,198,143,11,196,178,98,196,175,44,196,31,196,182,228,32,49,0,196,150,123,3,1,200,76,196,165,19,196,139,167,10, +2,196,138,237,196,129,175,196,171,160,30,8,196,129,33,196,187,242,18,12,2,1,65,12,196,147,105,196,180,60,13,0, +24,2,196,181,197,32,41,2,4,1,10,7,5,1,2,7,4,1,8,7,198,107,6,8,196,129,77,2,0,2,1,5, +12,196,135,36,198,45,31,2,20,1,32,67,2,196,176,182,198,132,48,198,176,14,5,2,196,184,217,196,162,96,32,52, +0,7,12,196,137,41,65,12,196,136,227,196,84,65,1,12,0,32,37,2,20,1,32,61,2,196,142,99,196,134,206,3, +1,196,44,4,8,196,185,57,4,0,196,42,198,137,82,32,33,6,196,188,119,3,0,196,184,89,196,183,56,25,2,198, +143,75,8,1,4,7,4,1,4,2,196,137,61,196,147,60,196,131,211,7,1,3,12,3,1,5,12,200,48,32,52,2, +16,1,32,49,2,196,135,63,197,1,133,71,196,119,196,173,61,196,187,129,13,1,13,12,196,148,128,196,74,196,183,160, +196,135,219,196,132,136,198,146,226,196,128,148,196,184,9,196,167,162,6,1,9,12,196,31,32,64,2,16,1,32,36,2, +196,142,53,196,133,214,196,133,132,6,2,196,185,140,196,173,161,196,146,233,17,1,12,6,18,1,7,12,196,69,32,70, +2,196,182,79,23,2,196,142,91,196,176,187,196,134,202,198,138,10,196,128,198,196,177,253,14,6,10,1,4,12,196,148, +143,5,6,196,128,178,32,76,2,14,1,25,2,196,181,146,2,7,8,1,5,7,3,1,196,80,2,8,196,187,188,196, +148,120,18,6,7,1,3,12,196,43,65,6,2,1,12,0,196,140,188,196,181,180,196,181,234,18,7,198,132,196,5,2, +196,176,104,4,8,2,1,2,0,196,118,32,44,6,6,1,196,129,123,6,6,196,42,32,86,2,14,1,198,135,240,196, +146,249,4,7,198,79,2,8,196,179,120,32,42,6,196,143,121,4,6,196,173,24,12,0,32,91,2,14,1,9,2,198, +141,4,3,1,4,7,4,1,196,75,65,8,196,110,26,6,196,181,94,3,6,196,35,32,96,2,13,1,196,135,212,6, +7,6,1,6,7,3,1,6,2,196,185,175,3,8,196,179,232,32,54,6,196,181,97,198,71,32,100,2,13,1,2,2, +30,7,196,134,209,198,186,157,32,81,6,196,181,120,3,6,196,180,6,196,128,211,32,105,2,196,131,3,196,148,41,198, +61,65,2,196,159,235,198,93,16,6,196,174,248,196,30,32,109,2,196,131,28,196,144,245,196,135,43,65,2,196,163,239, +196,150,51,32,66,6,196,177,133,196,58,32,113,2,12,1,196,139,137,65,1,196,135,72,196,177,43,32,85,6,196,25, +32,117,2,16,1,198,145,242,196,164,220,196,177,159,32,101,6,196,181,222,65,6,196,103,196,131,87,196,133,44,196,180, +124,32,87,6,196,20,65,6,2,1,13,6,196,129,251,32,126,2,32,32,1,32,111,6,202,21,32,131,2,23,1,32, +115,6,202,31,32,134,2,196,129,31,32,56,6,4,1,2,6,196,175,127,198,45,32,134,2,196,128,238,32,44,6,4, +1,3,6,196,182,135,3,6,198,65,32,131,2,196,21,2,6,5,1,198,19,2,1,12,6,196,139,212,32,126,2,196, +128,242,196,39,196,182,85,65,6,198,21,196,190,103,196,113,18,6,5,1,2,6,196,174,130,7,6,196,180,241,196,130, +228,32,121,2,196,23,4,6,200,74,3,6,196,18,32,120,2,6,1,7,6,29,1,32,96,6,4,1,4,6,3,1, +21,6,3,1,11,6,4,1,12,0,32,119,2,32,47,1,32,92,6,3,1,5,6,204,26,196,128,218,196,142,217,196, +189,107,32,88,6,196,149,202,196,180,253,196,42,196,181,166,11,0,32,116,2,14,1,196,187,219,196,149,116,21,6,196, +129,248,198,179,240,2,6,196,32,196,181,253,10,0,32,115,2,196,185,135,196,166,11,198,130,16,196,151,34,196,183,17, +196,128,136,12,6,196,100,196,31,32,114,2,196,176,153,196,186,139,196,131,40,196,183,39,13,6,195,1,129,140,196,28, +3,6,196,191,148,32,112,2,198,187,93,196,150,53,196,177,0,196,128,206,198,179,93,6,6,196,114,4,6,196,191,183, +32,111,2,198,187,63,196,150,80,7,6,196,181,201,12,6,2,1,196,113,13,6,196,175,98,65,6,4,1,8,0,32, +110,2,196,187,94,196,181,248,196,129,5,196,35,9,6,202,32,65,6,196,192,2,32,109,2,196,187,117,10,8,196,151, +166,2,6,196,179,220,196,150,89,65,6,196,181,192,196,57,2,6,3,1,7,0,32,109,2,198,178,123,32,38,6,12, +1,20,6,3,1,196,129,45,196,35,11,6,4,1,6,0,32,108,2,196,178,149,65,8,196,59,7,6,196,180,21,4, +6,200,31,65,6,3,1,6,0,32,107,2,196,177,72,196,130,232,12,6,196,146,31,3,6,198,25,65,1,5,0,32, +107,2,196,178,157,196,189,27,32,51,6,198,94,196,129,12,196,73,196,184,123,5,0,196,25,2,8,196,68,29,6,195, +1,130,113,198,181,121,14,6,4,1,4,0,206,22,196,76,4,0,32,106,2,196,52,196,129,180,30,6,3,1,196,129, +7,196,31,65,6,4,1,3,0,32,106,2,196,178,231,2,8,196,130,167,29,1,199,1,181,69,198,129,232,11,6,3, +1,3,0,196,188,103,196,140,104,196,179,4,65,8,198,181,200,65,6,32,35,1,196,129,222,5,6,195,1,130,205,10, +6,196,129,236,65,6,3,1,3,0,32,46,2,196,185,164,13,2,196,42,10,1,196,134,105,65,1,196,180,16,198,41, +196,129,77,16,6,196,192,157,32,44,2,13,1,196,181,59,196,182,89,196,185,59,198,133,234,32,37,8,198,179,206,196, +132,12,16,6,65,1,199,1,181,181,196,130,106,196,185,54,24,6,196,192,202,196,193,82,5,1,65,6,197,1,182,49, +196,134,215,196,138,225,11,2,196,185,212,65,8,198,130,52,196,152,57,196,151,39,196,130,94,198,130,212,198,130,65,196, +185,128,2,0,32,40,2,7,1,3,6,196,130,39,13,1,10,2,196,185,254,198,185,11,196,132,17,196,129,118,6,6, +2,1,197,1,129,208,16,6,3,1,2,0,32,39,2,6,1,5,6,196,130,79,3,6,13,1,7,2,196,184,132,2, +8,4,1,196,129,3,26,6,3,1,12,6,3,1,196,129,251,196,129,224,6,6,3,1,65,0,32,38,2,198,185,83, +7,6,196,153,19,10,1,196,139,1,26,8,196,130,56,25,6,196,131,74,204,41,32,36,2,5,1,196,43,17,6,8, +1,3,2,196,184,166,196,27,65,6,208,67,32,35,2,5,1,196,130,62,65,6,196,184,170,5,6,8,1,196,186,239, +10,8,196,153,245,32,35,6,200,97,7,6,196,185,158,4,6,3,1,32,34,2,198,40,196,181,197,196,184,25,196,181, +249,196,183,197,196,35,65,6,196,33,196,135,55,196,186,78,32,33,2,198,186,7,196,183,49,4,1,11,6,2,1,196, +181,158,196,177,39,5,8,196,36,65,6,3,1,7,6,2,1,3,6,196,128,168,198,185,234,5,6,3,1,32,32,2, +198,185,48,22,6,196,129,152,3,1,196,181,238,196,188,24,198,113,3,6,3,1,6,6,4,1,65,6,4,1,197,1, +131,30,22,6,3,1,31,2,196,185,88,196,130,131,6,6,3,1,4,6,4,1,3,6,196,178,161,196,149,152,196,188, +220,4,8,196,93,196,153,86,3,1,2,6,196,129,1,196,131,48,9,6,3,1,31,2,196,185,228,196,135,200,3,6, +2,1,198,49,196,154,217,13,6,196,188,189,8,8,196,11,32,32,6,3,1,5,6,198,48,196,128,132,2,6,2,1, +30,2,196,179,244,196,135,242,2,6,197,1,183,68,2,6,196,154,227,196,133,188,18,8,196,128,178,200,110,196,84,198, +114,27,6,2,1,30,2,197,1,186,73,13,6,196,36,196,132,97,26,6,196,185,75,196,119,7,6,196,183,116,3,1, +196,99,198,184,24,198,129,28,4,6,2,1,29,2,198,77,4,1,3,6,3,1,9,6,196,155,43,18,6,196,170,205, +196,128,160,8,6,196,92,9,6,2,1,14,6,196,135,160,65,6,2,1,28,2,196,180,104,196,128,207,65,6,200,47, +196,8,23,6,6,1,9,8,196,129,34,9,6,3,1,196,132,175,198,128,145,6,6,65,1,196,38,196,132,116,196,58, +32,50,6,6,1,196,190,125,32,74,6,196,31,198,128,214,196,28,27,2,196,180,166,65,6,196,85,6,6,196,32,65, +6,5,1,6,8,196,129,192,13,6,196,62,65,6,198,130,7,32,34,6,65,1,27,2,198,187,124,197,1,185,217,14, +6,2,1,196,152,194,32,64,6,5,1,3,8,196,129,232,196,129,216,198,133,21,200,43,200,35,196,129,44,32,40,6, +196,132,22,196,129,20,14,1,196,133,40,65,6,197,1,132,205,25,6,26,2,196,98,195,1,155,68,196,87,19,6,196, +132,166,23,6,19,1,196,130,221,196,131,105,15,6,26,2,198,89,12,6,196,22,196,129,83,26,6,7,1,3,3,7, +8,196,120,65,6,3,1,196,31,26,2,198,187,239,3,6,200,133,23,196,130,59,32,117,6,196,133,41,196,194,179,65, +3,198,167,117,65,1,2,6,196,188,12,196,129,162,29,6,25,2,196,128,191,65,6,3,1,3,6,198,184,180,17,6, +196,129,147,196,128,228,196,154,108,2,1,7,8,4,1,65,6,198,42,196,146,61,14,6,197,2,184,210,32,146,6,18, +1,6,3,3,1,7,8,3,1,196,27,200,25,4,1,11,3,2,1,7,3,198,19,196,181,101,196,131,192,20,6,19, +2,6,1,198,130,91,196,129,142,198,128,233,32,83,6,196,184,166,196,48,196,172,90,17,6,196,32,19,2,198,133,160, +4,6,198,129,2,32,82,6,196,182,73,3,3,200,28,202,22,196,178,44,196,82,196,132,40,196,64,198,34,2,1,5, +6,196,132,147,32,105,6,7,1,16,3,196,24,196,132,203,31,6,19,2,196,133,215,197,1,134,36,32,140,6,12,1, +196,114,12,8,196,188,200,2,6,196,29,200,26,196,184,186,196,191,199,196,191,196,198,21,200,130,165,32,38,6,19,2, +197,1,186,214,32,153,6,198,24,3,1,196,130,45,9,6,198,19,196,130,218,7,3,198,186,23,196,188,46,197,1,130, +230,196,134,94,15,6,198,41,6,6,198,193,212,10,8,196,135,69,197,1,185,223,32,36,6,8,1,11,2,196,62,6, +6,198,177,0,11,8,196,135,92,196,129,15,14,6,27,1,196,155,68,32,174,6,2,1,65,6,196,192,36,196,189,11, +198,130,153,32,36,6,29,1,13,6,3,1,32,183,6,14,1,3,8,6,1,17,6,2,1,5,6,3,1,32,42,6, +196,178,186,196,132,214,196,132,122,32,160,6,22,1,198,26,196,136,73,8,6,25,2,196,130,70,32,71,6,7,1,196, +131,17,32,39,6,11,1,198,131,127,198,133,74,30,6,26,2,3,1,196,76,32,99,6,17,1,65,6,196,130,56,5, +6,197,1,135,32,32,32,6,198,26,196,134,212,21,1,32,80,6,196,18,198,38,196,134,199,198,192,243,196,130,149,198, +133,219,32,32,6,198,55,32,89,6,196,188,65,7,8,196,133,149,198,187,8,13,6,196,125,8,6,26,2,198,131,102, +32,57,6,196,180,46,6,8,202,26,27,2,196,130,127,32,47,6,196,192,38,196,41,10,6,196,182,198,196,38,196,21, +32,45,6,196,33,4,8,196,108,196,134,65,196,54,27,2,198,132,24,196,133,189,20,6,196,22,4,8,196,133,226,198, +41,65,6,28,2,196,130,121,196,129,19,196,169,196,198,133,246,9,6,196,183,0,196,131,23,65,6,28,2,198,132,221, +24,6,196,180,55,10,8,196,130,227,4,6,196,182,149,196,121,65,6,29,2,196,189,222,2,1,196,131,118,27,6,196, +192,181,196,134,40,8,6,196,182,176,196,53,29,2,196,190,68,196,190,210,198,186,178,32,45,6,196,192,208,198,134,69, +196,190,84,198,135,206,32,43,6,30,2,7,1,6,6,196,131,178,196,135,62,19,6,196,193,25,196,134,98,65,6,196, +157,105,196,132,138,196,134,5,196,115,31,2,9,1,197,1,191,16,196,134,92,196,186,162,32,42,8,196,134,254,196,133, +105,196,189,245,11,6,196,159,27,196,128,148,65,6,196,192,54,196,137,179,10,6,196,193,44,6,8,196,135,30,18,1, +21,6,196,159,55,196,133,249,28,6,196,133,85,196,135,99,23,6,196,193,116,7,8,196,29,20,6,196,159,46,196,128, +176,65,6,32,44,2,196,135,129,198,26,2,8,198,136,48,198,187,224,196,187,84,196,117,196,187,175,32,42,6,32,47, +2,198,191,37,2,6,196,158,26,196,188,112,2,8,196,130,107,8,1,12,6,6,1,2,6,196,9,5,6,196,159,146, +198,129,232,11,6,32,47,2,196,182,197,65,1,196,186,240,196,158,21,196,193,66,5,8,196,129,200,9,1,196,138,102, +197,1,184,111,13,6,196,132,111,6,6,196,183,30,65,1,196,185,2,2,1,196,148,182,196,189,128,196,132,136,196,80, +196,188,33,196,191,114,196,133,110,196,136,230,196,82,28,6,196,38,196,138,24,196,182,94,4,1,65,8,196,185,124,2, +8,198,189,215,196,158,110,195,1,159,97,15,6,196,133,226,198,187,238,32,40,6,32,49,2,196,138,63,10,2,196,108, +12,8,3,1,5,6,196,191,75,4,6,6,1,198,187,186,196,75,7,6,196,133,81,15,6,32,51,2,196,147,61,25, +2,25,8,196,190,13,6,8,196,133,11,196,160,64,196,188,140,196,188,95,196,133,101,196,132,164,196,159,35,65,6,32, +93,2,23,8,196,133,167,6,8,198,153,210,196,134,65,196,187,252,196,141,92,196,191,73,196,132,186,196,133,140,198,134, +22,196,128,178,196,137,129,2,6,32,93,2,198,171,162,198,175,120,32,34,8,196,60,196,187,218,196,137,166,196,160,112, +6,6,3,1,198,134,60,196,192,27,196,152,195,26,6,32,93,2,198,194,215,196,158,175,12,6,17,1,11,6,198,34, +2,6,196,137,88,196,151,214,26,6,32,93,2,198,188,208,196,146,108,196,132,0,196,191,140,2,1,11,6,196,65,65, +6,3,1,2,6,198,135,30,196,190,157,6,6,32,93,2,198,170,160,196,194,233,3,8,198,28,3,1,65,6,8,1, +200,38,65,1,196,34,196,188,198,10,6,32,94,2,198,171,63,198,184,13,196,131,90,196,191,199,65,6,198,64,65,1, +198,133,207,3,6,196,150,249,196,142,21,196,33,196,193,250,196,129,107,196,130,50,12,6,196,134,47,6,6,196,25,32, +94,2,196,195,209,3,8,196,139,201,196,24,65,6,196,23,2,1,196,188,231,13,6,32,94,2,196,196,19,65,8,198, +102,196,134,126,8,6,196,137,178,65,6,197,1,189,216,7,6,198,151,44,196,138,113,65,6,32,95,2,196,196,11,196, +190,65,196,156,155,32,39,6,196,35,196,138,81,198,135,37,5,6,196,189,4,196,185,126,6,6,198,34,196,143,19,196, +128,235,196,138,212,20,6,196,138,110,198,139,5,196,129,208,8,6,32,96,2,196,196,68,65,8,196,184,255,196,190,101, +65,6,198,59,4,6,196,160,191,65,6,32,96,2,196,186,160,9,8,196,137,166,196,189,25,10,1,196,159,210,198,134, +239,198,135,121,198,189,39,21,6,32,96,2,196,191,112,196,136,239,196,191,27,9,1,196,190,46,28,6,196,160,97,198, +191,213,196,157,113,196,193,217,30,6,32,97,2,196,186,250,6,8,196,140,139,196,189,88,4,1,196,134,162,12,6,196, +191,66,65,1,8,7,196,161,87,65,6,32,98,2,196,130,249,196,130,249,196,193,91,65,1,196,32,197,1,191,145,198, +155,4,29,6,32,99,2,196,187,81,198,137,245,9,6,196,129,155,8,6,4,1,7,6,3,1,7,6,198,187,244,29, +6,32,101,2,32,47,1,196,131,47,20,6,196,138,79,4,6,3,1,196,138,242,196,190,191,29,6,32,102,2,32,55, +1,32,55,6,196,138,103,196,190,149,198,24,32,102,2,18,1,196,141,58,196,138,57,32,80,6,196,193,12,198,136,51, +198,158,235,29,6,32,103,2,196,129,67,4,6,196,138,200,32,68,6,196,26,65,1,196,192,44,9,6,32,104,2,3, +1,32,144,6,10,1,4,6,3,1,7,6,3,1,9,7,4,1,29,6,32,104,2,4,1,32,142,6,18,1,8,6, +3,1,6,7,5,1,30,6,32,105,2,196,132,42,32,63,6,196,148,111,65,2,9,1,8,6,198,158,105,197,1,140, +87,3,6,32,105,2,4,1,32,140,6,4,1,13,2,2,1,9,6,11,1,14,6,3,1,15,6,32,106,2,196,137, +25,198,130,240,13,6,196,201,62,196,131,79,196,194,0,196,140,79,196,24,196,131,12,196,131,114,10,6,198,50,196,194, +18,198,141,23,7,6,32,107,2,196,137,68,198,137,57,32,37,6,196,72,200,144,69,196,137,71,6,6,32,107,2,196, +41,6,1,196,130,159,196,136,119,2,1,14,2,196,162,245,196,129,228,3,6,32,108,2,196,131,179,8,6,5,1,196, +130,122,196,136,147,196,149,187,198,53,32,109,2,196,131,200,7,6,196,129,84,27,6,5,1,16,2,198,47,32,110,2, +196,131,109,5,6,196,129,74,27,6,196,185,224,198,90,32,112,2,196,131,127,3,6,196,129,92,26,6,196,185,242,2, +2,198,85,32,113,2,196,140,77,12,6,196,129,141,25,6,196,21,198,127,32,115,2,196,131,164,5,1,196,130,245,28, +6,196,75,3,2,198,121,196,140,118,22,1,21,6,4,1,196,144,184,28,6,5,1,3,2,10,1,7,2,198,128,145, +196,26,9,1,198,136,179,2,6,6,7,65,6,196,134,173,13,6,25,1,198,128,193,32,117,2,9,1,196,150,85,24, +1,32,63,6,26,1,198,128,211,32,153,2,31,1,32,53,6,5,1,11,0,11,1,200,145,40,198,136,212,2,6,196, +183,77,196,193,242,17,6,196,145,47,10,0,196,129,125,196,136,72,32,176,2,196,108,15,6,196,19,196,144,1,196,128, +223,16,6,196,183,151,196,138,81,196,164,92,28,0,198,137,2,196,195,69,4,6,32,172,2,15,1,196,158,92,196,14, +65,6,196,202,40,198,24,32,167,2,14,1,196,154,173,18,1,31,0,196,144,198,196,135,115,32,163,2,196,153,217,196, +204,21,196,92,10,0,196,20,32,158,2,196,160,41,5,0,196,183,42,15,0,196,35,32,155,2,12,1,32,100,0,3, +1,29,6,4,1,17,6,196,204,213,196,181,189,196,204,72,29,6,196,141,240,196,204,227,196,154,50,32,68,0,196,32, +196,141,254,32,153,2,198,180,110,196,184,36,32,35,0,196,18,32,152,2,196,200,241,196,203,142,196,204,240,27,6,196, +193,64,32,151,2,196,194,75,196,203,159,196,204,134,196,18,32,150,2,5,1,65,3,196,136,70,196,184,85,29,0,196, +138,27,196,193,99,32,149,2,5,1,3,3,3,1,10,8,8,1,32,90,0,4,1,32,48,6,32,148,2,196,192,14, +198,194,0,196,146,239,32,46,0,196,135,143,6,6,32,147,2,6,1,4,3,196,201,205,196,154,178,32,55,0,196,22, +32,146,2,6,1,5,3,196,201,13,198,147,112,198,40,32,146,2,196,189,227,196,143,109,196,184,179,24,0,196,130,214, +6,6,32,146,2,196,191,112,196,159,143,17,8,196,22,196,205,82,32,47,6,32,145,2,196,194,142,196,201,35,2,8, +198,20,32,144,2,4,1,10,3,196,201,79,196,15,32,143,2,198,137,5,196,187,193,196,64,2,0,196,136,1,4,6, +196,130,190,196,199,40,196,188,108,196,201,111,196,153,223,32,49,0,196,24,196,130,210,196,199,26,65,2,198,201,48,196, +182,250,22,0,196,139,1,7,6,32,104,2,11,1,24,2,5,1,15,3,3,1,17,8,3,1,32,90,0,8,1,32, +38,6,2,1,196,28,23,2,196,25,65,3,202,25,198,138,162,3,1,196,43,22,2,196,16,65,3,200,40,65,0,196, +195,70,196,193,221,4,1,32,101,2,196,134,18,2,2,196,191,244,196,24,65,0,198,205,174,19,1,65,13,32,101,2, +25,1,8,2,5,1,65,2,19,3,196,25,6,0,2,13,32,34,1,4,13,196,144,95,19,1,5,2,198,25,198,70, +6,0,9,13,196,169,93,5,13,22,2,12,1,196,145,145,26,1,4,2,198,49,196,180,15,196,151,228,32,50,0,32, +41,13,196,26,32,43,2,27,1,21,5,4,1,65,2,198,76,196,129,45,196,128,157,196,56,32,33,13,196,52,28,2, +32,34,1,29,5,9,1,196,98,198,138,9,9,8,198,27,196,72,196,187,172,29,1,32,40,5,196,166,61,8,3,196, +202,101,196,128,203,5,0,32,43,13,196,98,32,40,1,32,40,5,10,1,196,150,14,28,3,200,27,196,156,133,32,33, +1,32,50,5,12,1,65,5,196,204,36,65,3,196,47,4,0,32,44,13,196,189,18,65,1,32,67,5,196,25,2,5, +196,158,233,198,23,11,2,19,1,32,82,5,200,17,11,2,7,1,32,72,5,196,157,252,65,5,16,1,3,5,196,159, +7,196,202,199,196,130,11,65,0,32,45,13,11,2,196,168,14,32,55,5,3,1,10,5,21,1,196,31,196,181,168,2, +8,2,1,196,159,189,196,159,195,196,128,133,2,13,196,35,31,5,196,151,8,8,5,200,36,196,202,84,65,8,196,129, +41,19,1,32,32,0,32,46,13,196,28,196,168,211,196,151,36,196,154,231,8,5,23,1,4,5,196,161,251,196,192,219, +196,181,83,196,195,162,32,33,1,31,0,32,47,13,196,99,21,5,4,1,6,5,196,44,2,5,3,1,5,5,26,1, +3,5,196,41,2,1,198,202,46,32,57,3,13,1,2,5,196,183,43,7,13,11,2,196,128,163,196,155,9,196,168,182, +198,128,169,196,155,18,4,5,196,160,201,196,156,13,196,169,54,27,1,196,156,21,28,3,196,182,122,196,204,93,196,204, +10,200,54,11,2,196,161,156,196,151,34,134,81,196,149,96,134,86,4,5,3,1,3,5,2,1,197,1,155,70,198,49, +27,3,13,1,196,159,126,32,40,3,28,1,8,5,196,183,20,9,13,196,153,147,196,159,43,196,151,83,3,1,2,5, +15,1,198,156,100,200,52,65,1,198,56,196,151,101,196,165,246,20,1,3,5,198,162,182,2,1,196,197,130,196,161,77, +196,167,90,21,1,15,5,196,184,251,2,13,15,1,12,5,5,1,198,156,148,6,1,198,156,153,198,105,195,1,156,41, +198,111,197,1,155,217,18,5,13,1,196,156,172,25,3,8,1,196,205,216,196,160,225,28,3,11,1,29,5,196,188,162, +11,13,196,55,3,1,5,5,3,1,11,5,3,1,7,5,2,1,6,5,3,1,3,5,3,1,196,118,66,1,5,200, +129,139,198,190,51,65,1,2,3,3,1,3,3,8,1,198,182,128,25,3,196,169,129,65,5,7,1,4,5,196,65,196, +194,170,65,3,196,61,65,5,214,69,196,128,217,2,5,4,1,198,156,32,196,166,222,14,1,196,157,27,23,3,198,131, +183,196,199,51,32,58,3,198,156,95,196,161,179,196,33,196,156,146,6,1,196,58,12,3,196,53,196,170,98,196,162,245, +198,117,196,129,27,65,5,196,170,111,198,156,130,200,156,94,196,150,131,5,1,196,157,87,22,3,196,195,235,198,188,235, +196,205,195,5,3,198,156,118,5,1,2,5,198,156,164,198,8,65,1,196,168,244,32,37,13,196,68,4,1,65,5,196, +163,122,198,156,145,196,121,7,1,198,157,139,196,35,65,1,4,5,2,1,198,129,39,196,190,146,196,193,203,198,188,31, +196,65,2,1,196,152,158,2,1,196,30,196,66,2,5,198,169,50,32,37,13,196,61,196,168,183,196,128,235,196,129,101, +65,1,198,157,196,2,5,196,155,209,198,169,77,20,1,3,5,196,197,104,196,168,86,198,201,1,32,58,3,2,1,65, +5,198,129,14,2,5,3,1,65,5,2,1,3,5,4,1,65,5,3,1,198,17,2,5,196,129,60,65,13,13,3,196, +128,252,196,162,254,2,5,196,129,56,196,128,198,2,5,2,1,196,80,6,1,200,129,68,24,1,3,5,198,160,245,198, +189,198,196,187,207,32,51,3,202,76,13,1,3,5,2,1,196,129,54,65,13,196,57,196,130,135,10,5,197,1,171,41, +196,50,196,156,201,196,129,1,26,1,196,158,76,18,3,196,168,209,198,129,108,32,68,3,202,124,9,1,65,5,198,130, +43,196,128,239,65,13,196,171,117,29,5,197,1,171,88,2,1,197,1,129,157,196,171,46,196,131,36,196,169,252,196,129, +44,17,3,196,165,252,32,70,3,200,128,164,196,153,134,66,1,5,196,129,254,196,128,231,65,13,14,3,196,129,169,7, +5,196,55,3,5,3,1,2,5,5,1,9,5,200,54,196,193,21,196,185,108,32,36,3,6,1,4,5,8,1,9,5, +4,1,2,5,3,1,2,5,196,185,83,13,13,136,133,101,10,3,202,55,65,5,196,46,196,128,162,11,1,4,5,196, +201,75,65,3,198,206,181,32,69,3,6,1,4,5,3,1,65,5,4,1,196,59,3,5,200,53,198,207,141,196,163,134, +196,71,196,171,187,198,101,196,95,198,158,24,14,5,196,157,195,2,5,196,201,127,198,206,98,196,170,123,196,190,89,196, +172,71,196,129,190,14,1,196,154,33,196,186,142,5,13,196,207,215,7,5,16,1,8,5,198,128,148,202,130,108,197,1, +130,60,21,5,198,207,21,196,123,198,130,99,196,171,162,196,158,175,2,1,65,5,198,130,251,196,129,245,32,39,13,196, +193,197,196,131,145,13,1,196,169,49,11,1,200,130,155,196,163,248,32,42,5,196,202,54,196,206,196,198,130,149,32,66, +3,198,129,224,196,131,212,196,171,8,65,1,198,154,134,32,36,13,196,162,20,65,3,196,172,131,196,131,118,10,1,7, +5,196,172,65,31,5,9,1,11,3,196,133,84,196,207,239,14,3,196,130,78,196,132,5,8,5,196,128,243,6,13,65, +4,196,194,39,196,172,100,196,65,196,164,82,6,5,196,132,61,26,5,196,182,12,196,189,227,18,3,196,47,198,158,238, +198,131,25,15,1,32,45,13,2,4,4,1,11,3,3,1,32,85,5,24,1,13,3,4,1,19,3,4,1,32,67,3, +2,1,12,5,24,1,32,46,13,3,4,198,34,32,74,5,28,1,18,3,196,193,44,196,208,89,196,194,126,13,1,2, +3,32,55,13,4,4,196,134,66,196,128,137,13,5,32,32,1,23,3,197,1,202,39,196,208,172,19,1,196,177,147,32, +41,13,4,4,5,1,9,3,4,1,32,55,5,27,1,32,36,3,4,1,23,3,3,1,32,65,3,13,1,16,3,32, +56,13,5,4,198,33,32,33,5,32,37,1,196,176,43,196,210,230,196,188,65,5,3,32,57,13,7,4,4,1,8,3, +5,1,18,5,32,44,1,32,53,3,3,1,26,3,3,1,32,92,3,32,57,13,8,4,198,29,196,131,75,196,170,127, +32,51,3,196,211,26,196,174,73,196,176,161,196,192,34,32,58,13,9,4,198,57,196,176,119,32,68,3,196,208,254,196, +207,20,11,1,196,160,198,32,57,13,9,4,196,187,209,196,175,230,10,3,198,209,3,29,1,196,22,10,4,196,173,211, +10,3,196,89,196,174,135,32,52,3,28,1,7,5,196,189,152,23,13,11,4,196,173,235,7,3,196,209,246,196,209,11, +196,174,163,32,32,5,196,132,185,18,13,12,4,196,177,186,196,210,68,196,193,203,9,3,198,28,196,131,35,17,13,13, +4,196,177,206,196,179,7,196,170,205,22,3,196,209,58,2,3,198,130,141,3,5,196,165,247,6,1,196,130,219,17,13, +13,4,196,129,72,196,209,49,22,1,5,3,196,195,108,2,3,200,38,2,1,65,5,8,1,65,5,198,35,14,4,196, +189,185,196,203,83,196,146,86,14,3,196,31,196,65,196,174,150,196,131,128,197,1,31,196,94,65,13,14,4,196,211,216, +196,179,123,11,1,31,3,196,60,196,209,151,196,207,221,196,130,214,196,132,81,13,1,32,56,13,15,4,198,208,232,32, +64,1,196,174,145,27,3,196,199,77,196,207,240,7,3,3,1,196,130,248,196,133,141,196,184,4,32,38,13,15,4,15, +1,196,189,87,196,191,179,196,128,232,196,172,219,11,1,196,160,218,200,132,105,196,95,16,4,15,1,196,211,2,196,183, +41,7,3,198,212,62,32,45,1,196,160,244,196,132,97,198,131,48,197,1,132,12,196,129,9,25,4,196,184,178,32,56, +3,196,129,120,196,174,118,196,166,180,13,1,5,5,200,161,31,198,133,185,65,5,196,128,160,65,13,26,4,196,178,202, +19,3,196,196,26,196,167,69,12,0,196,161,55,198,161,62,196,132,172,198,133,96,196,134,71,18,13,27,4,196,190,131, +9,3,198,203,77,32,46,0,196,161,42,196,132,199,198,134,27,32,61,13,27,4,196,58,196,195,112,10,1,196,213,152, +196,165,53,196,131,86,196,49,3,13,28,4,196,48,196,202,5,196,212,30,196,213,203,196,174,254,32,45,13,196,20,198, +199,160,198,187,229,196,193,18,196,160,90,32,55,13,28,4,196,175,134,196,209,223,196,175,8,196,133,179,198,156,10,196, +213,53,196,135,60,21,13,29,4,196,128,141,198,210,125,3,1,3,3,3,1,198,153,211,19,0,32,66,13,30,4,196, +128,163,198,130,217,198,191,248,196,204,123,32,68,0,32,67,13,31,4,196,128,151,4,1,9,3,5,1,200,145,252,196, +193,218,3,0,32,67,13,31,4,196,128,205,198,130,113,7,3,196,146,15,196,155,99,18,0,32,67,13,30,4,196,129, +4,196,193,14,196,209,33,8,3,196,146,39,196,155,156,15,0,32,68,13,30,4,196,180,172,32,45,3,3,1,4,3, +196,170,153,10,8,196,191,216,196,135,160,22,13,65,1,30,4,196,180,199,196,28,2,3,196,181,34,65,3,3,1,11, +8,196,194,46,32,67,13,2,1,29,4,196,191,88,21,3,3,1,65,3,196,131,195,202,27,29,4,3,1,65,3,196, +180,245,196,176,141,196,131,212,200,46,32,66,13,3,1,28,4,4,1,196,200,105,32,108,3,11,1,198,212,12,196,165, +53,32,34,0,196,65,65,1,196,26,196,192,131,32,99,3,196,195,34,2,3,196,187,220,196,188,245,196,209,206,16,1, +7,0,196,57,65,1,27,4,196,213,191,196,186,98,196,179,170,196,202,130,196,201,211,196,137,97,196,38,32,55,1,7, +0,32,65,13,4,1,65,14,27,4,3,1,196,128,201,196,176,250,32,49,1,3,3,196,205,202,196,70,32,44,1,17, +3,196,64,65,14,26,4,196,201,146,196,198,90,196,130,110,9,3,196,205,229,198,185,93,20,1,196,212,235,196,133,49, +198,64,65,14,26,4,198,212,249,32,51,1,32,34,3,196,200,129,196,197,42,196,185,125,196,171,49,196,195,12,196,132, +0,9,13,4,1,3,14,25,4,32,61,1,32,62,3,198,167,8,65,1,9,8,196,170,184,3,1,196,173,195,9,3, +32,64,13,196,33,25,4,196,193,213,10,3,196,174,58,196,168,36,196,210,209,8,5,196,213,35,196,27,65,14,25,4, +196,129,22,2,3,196,129,211,65,3,196,187,108,196,174,222,196,168,249,196,176,22,196,132,2,7,13,196,191,140,25,4, +196,131,140,196,212,53,2,1,196,193,129,196,167,197,9,3,196,170,7,4,5,196,147,250,32,63,13,196,176,128,198,130, +132,7,3,196,138,191,196,214,33,196,177,27,196,191,166,196,32,196,135,165,3,3,32,64,13,196,191,122,26,4,196,130, +196,7,3,196,213,182,196,214,37,6,1,198,201,198,196,177,221,17,5,196,171,26,196,63,65,14,27,4,196,130,226,6, +3,198,196,111,196,215,133,6,1,196,191,200,17,3,196,164,125,11,5,196,171,58,32,63,13,196,177,124,28,4,196,131, +4,196,196,117,198,212,110,196,214,37,8,3,196,137,59,196,134,103,198,135,29,24,13,196,191,29,196,130,94,200,195,165, +196,134,66,65,3,196,211,204,2,3,196,178,22,196,171,40,2,5,196,192,222,198,190,238,196,31,196,215,34,196,215,152, +198,164,77,196,137,60,196,136,192,20,13,196,190,194,196,130,104,4,3,200,195,217,16,3,196,132,207,65,3,3,1,5, +5,196,170,216,196,53,198,190,161,32,32,4,196,131,48,196,132,140,8,3,200,24,19,13,4,1,11,14,32,32,4,196, +193,112,196,215,17,196,132,166,196,196,56,134,46,2,5,196,171,6,196,134,12,2,13,196,135,160,198,129,122,9,14,32, +32,4,4,1,6,3,196,178,218,25,1,200,39,196,137,183,196,187,253,27,13,196,190,100,32,33,4,196,165,204,196,185, +136,196,192,62,32,36,1,196,29,15,1,32,44,13,196,190,56,196,171,1,26,1,196,203,55,196,133,202,196,135,187,198, +136,222,10,5,13,1,3,15,196,194,146,196,82,2,14 +}; + +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe -b 1 0 -s 5 cityscroll2.gif + +const unsigned short data_cityscroll2_gif::pal[19] PROGMEM_FAR = +{ +46717,0,34090,48599,44275,29548,47841,61342,27697,56851,57050,57051,19291,65370,63463,64106,46026,44110,22753 +}; + +const unsigned char data_cityscroll2_gif::data[19853] PROGMEM_FAR = +{ +39,3,0,17,12,14,14,13,13,12,13,10,12,9,14,13,14,15,12,11,15,11,14,17,15,11,13,12,11,13,13,12, +11,11,9,9,9,9,9,10,7,8,8,10,12,11,12,13,12,13,14,12,11,12,13,11,10,9,11,10,12,10,10,9, +10,8,8,9,9,8,8,8,6,5,9,7,6,7,7,7,4,9,7,8,9,9,10,9,10,9,10,9,10,6,8,5, +6,9,6,6,6,8,8,4,6,7,7,9,7,8,7,7,8,10,10,10,14,19,12,13,8,10,10,9,9,8,8,10, +12,12,13,14,14,16,14,14,12,12,10,8,11,10,14,13,14,14,17,17,15,17,17,16,16,13,16,16,20,18,20,17, +23,21,15,18,16,19,15,19,16,15,14,15,12,9,15,13,12,11,10,7,10,6,6,6,7,10,8,9,10,10,8,10, +9,11,9,10,7,10,11,7,6,13,10,9,9,14,11,7,11,12,9,13,8,10,9,5,10,11,16,12,12,9,14,11, +12,6,12,12,7,13,11,13,13,15,11,12,9,9,13,11,9,10,13,9,12,11,10,10,9,9,14,14,14,8,12,16, +10,12,10,13,15,14,10,10,13,12,11,11,14,21,13,16,14,14,12,15,16,16,14,13,18,14,14,14,12,15,16,15, +16,14,18,16,12,15,11,16,10,11,13,13,7,4,7,13,5,5,11,8,6,17,13,11,11,17,13,13,13,13,12,9, +10,10,7,8,12,7,11,12,12,17,11,10,11,13,14,15,18,19,12,17,10,14,12,14,11,8,9,12,13,13,14,13, +14,12,13,8,12,13,11,5,9,9,8,8,8,9,9,8,7,8,10,6,6,6,6,6,5,4,7,4,4,5,3,4, +3,5,5,6,6,6,6,7,7,7,8,8,8,6,6,6,6,6,6,5,5,4,4,4,4,4,4,4,4,4,4,4, +4,4,4,3,3,4,4,5,5,4,4,4,7,9,8,8,8,9,9,6,5,9,9,8,5,10,6,6,7,9,10,6, +9,6,8,7,6,9,7,10,7,4,10,6,4,5,8,5,7,6,7,7,6,10,7,7,6,5,8,9,9,6,9,11, +6,6,4,9,5,6,7,5,5,9,6,4,7,8,5,5,6,6,8,6,5,7,6,5,6,10,5,4,8,6,4,5, +7,8,9,10,10,11,12,10,9,11,10,10,11,15,11,14,8,12,13,15,15,13,13,15,14,17,13,12,11,12,13,11, +8,13,11,9,13,10,12,9,10,9,9,14,12,11,9,13,11,10,7,7,9,11,10,6,4,6,7,7,9,9,4,5, +5,5,1,1,3,5,6,4,4,5,5,5,6,5,4,3,3,6,5,6,3,3,7,9,7,9,7,7,11,12,9,13, +11,8,14,13,9,7,16,10,11,12,11,9,10,8,9,8,5,8,6,11,13,10,13,9,16,14,12,7,13,15,12,10, +11,7,10,11,16,12,8,13,12,8,7,6,8,15,14,8,12,14,18,10,12,13,12,11,11,12,10,11,12,12,8,11, +13,16,10,11,9,11,14,13,13,14,9,10,4,9,9,11,10,8,10,7,13,10,10,11,9,9,8,9,7,7,7,13, +6,8,4,4,4,6,6,4,1,3,6,8,1,1,3,4,7,3,2,5,7,1,4,3,1,4,1,1,1,4,4,3, +3,3,3,4,3,3,3,4,3,5,3,3,4,3,3,3,3,4,3,3,3,3,4,3,3,3,4,5,4,5,5,6, +5,4,4,4,4,3,4,4,4,4,4,4,4,3,3,2,2,1,1,1,1,1,2,1,2,5,6,6,5,5,5,6, +6,5,3,6,6,6,8,6,7,8,0,0,37,0,63,0,94,0,122,0,150,0,178,0,204,0,234,0,0,1,24,1, +42,1,73,1,103,1,133,1,165,1,191,1,216,1,248,1,16,2,46,2,82,2,113,2,136,2,166,2,193,2,217,2, +247,2,21,3,47,3,73,3,99,3,121,3,141,3,162,3,182,3,201,3,224,3,240,3,3,4,21,4,46,4,74,4, +100,4,127,4,156,4,184,4,214,4,245,4,16,5,40,5,67,5,97,5,121,5,144,5,165,5,190,5,212,5,238,5, +3,6,25,6,46,6,69,6,88,6,105,6,124,6,144,6,161,6,178,6,195,6,208,6,219,6,238,6,255,6,11,7, +26,7,40,7,54,7,62,7,81,7,95,7,113,7,133,7,153,7,175,7,195,7,218,7,239,7,5,8,25,8,46,8, +59,8,77,8,89,8,102,8,121,8,135,8,148,8,162,8,180,8,199,8,209,8,222,8,239,8,254,8,18,9,35,9, +53,9,69,9,84,9,103,9,127,9,149,9,172,9,207,9,247,9,19,10,51,10,70,10,94,10,120,10,140,10,165,10, +186,10,209,10,237,10,11,11,38,11,69,11,100,11,131,11,168,11,198,11,229,11,0,12,27,12,53,12,73,12,103,12, +128,12,158,12,189,12,221,12,252,12,33,13,73,13,108,13,146,13,184,13,221,13,3,14,36,14,74,14,112,14,154,14, +197,14,243,14,29,15,76,15,120,15,156,15,200,15,241,15,28,16,65,16,106,16,143,16,179,16,213,16,249,16,23,17, +44,17,77,17,107,17,138,17,165,17,190,17,211,17,235,17,248,17,9,18,26,18,43,18,68,18,88,18,112,18,138,18, +161,18,179,18,202,18,221,18,247,18,13,19,38,19,55,19,79,19,106,19,124,19,139,19,173,19,199,19,222,19,243,19, +22,20,47,20,66,20,91,20,121,20,142,20,171,20,192,20,217,20,239,20,253,20,20,21,47,21,85,21,115,21,144,21, +166,21,202,21,230,21,3,22,16,22,48,22,78,22,96,22,128,22,154,22,186,22,216,22,251,22,23,23,52,23,76,23, +99,23,134,23,161,23,183,23,212,23,246,23,13,24,41,24,68,24,92,24,115,24,136,24,159,24,192,24,224,24,3,25, +22,25,52,25,91,25,117,25,149,25,173,25,204,25,241,25,18,26,43,26,68,26,100,26,130,26,156,26,183,26,216,26, +4,27,35,27,73,27,108,27,142,27,170,27,205,27,240,27,23,28,56,28,88,28,128,28,159,28,191,28,222,28,252,28, +31,29,65,29,98,29,133,29,166,29,206,29,245,29,19,30,54,30,81,30,119,30,143,30,168,30,199,30,230,30,246,30, +0,31,18,31,47,31,58,31,68,31,92,31,111,31,123,31,163,31,192,31,215,31,238,31,18,32,52,32,86,32,117,32, +151,32,182,32,204,32,229,32,253,32,16,33,36,33,65,33,83,33,110,33,139,33,169,33,205,33,230,33,253,33,22,34, +51,34,83,34,119,34,159,34,205,34,233,34,17,35,40,35,71,35,99,35,128,35,152,35,171,35,193,35,219,35,252,35, +26,36,57,36,87,36,121,36,147,36,176,36,195,36,224,36,254,36,23,37,35,37,56,37,78,37,98,37,119,37,140,37, +161,37,182,37,200,37,217,37,235,37,4,38,19,38,34,38,51,38,66,38,81,38,95,38,106,38,123,38,135,38,145,38, +159,38,166,38,176,38,183,38,196,38,210,38,226,38,242,38,1,39,17,39,34,39,52,39,70,39,89,39,108,39,127,39, +142,39,159,39,176,39,193,39,209,39,224,39,237,39,248,39,3,40,13,40,23,40,33,40,43,40,54,40,65,40,76,40, +87,40,98,40,109,40,119,40,129,40,139,40,147,40,155,40,165,40,175,40,187,40,199,40,210,40,222,40,234,40,250,40, +15,41,34,41,54,41,73,41,94,41,114,41,128,41,139,41,159,41,178,41,197,41,208,41,231,41,244,41,2,42,18,42, +38,42,62,42,76,42,96,42,109,42,127,42,145,42,159,42,181,42,198,42,222,42,238,42,247,42,14,43,29,43,38,43, +49,43,68,43,81,43,98,43,114,43,131,43,150,43,165,43,192,43,210,43,226,43,241,43,252,43,14,44,34,44,56,44, +69,44,90,44,118,44,132,44,148,44,157,44,182,44,193,44,207,44,224,44,235,44,247,44,15,45,29,45,39,45,57,45, +78,45,90,45,103,45,118,45,132,45,153,45,169,45,181,45,200,45,217,45,230,45,245,45,15,46,28,46,38,46,60,46, +73,46,84,46,96,46,112,46,129,46,149,46,170,46,192,46,216,46,244,46,11,47,31,47,56,47,80,47,103,47,127,47, +161,47,186,47,217,47,236,47,4,48,31,48,65,48,103,48,138,48,171,48,211,48,245,48,25,49,57,49,87,49,112,49, +142,49,174,49,200,49,217,49,249,49,20,50,40,50,70,50,93,50,121,50,144,50,167,50,189,50,210,50,248,50,20,51, +47,51,69,51,101,51,125,51,150,51,169,51,186,51,210,51,239,51,6,52,20,52,30,52,46,52,64,52,81,52,104,52, +127,52,137,52,149,52,161,52,172,52,174,52,176,52,183,52,195,52,210,52,220,52,230,52,242,52,254,52,10,53,25,53, +38,53,47,53,54,53,61,53,76,53,90,53,105,53,112,53,119,53,135,53,156,53,174,53,195,53,212,53,229,53,0,54, +28,54,50,54,79,54,102,54,122,54,159,54,187,54,207,54,223,54,5,55,30,55,55,55,81,55,107,55,127,55,151,55, +169,55,189,55,209,55,221,55,238,55,251,55,21,56,54,56,78,56,110,56,129,56,166,56,203,56,227,56,245,56,21,57, +59,57,89,57,111,57,141,57,158,57,182,57,207,57,243,57,19,58,39,58,73,58,103,58,124,58,142,58,156,58,176,58, +210,58,249,58,13,59,44,59,79,59,119,59,145,59,178,59,211,59,239,59,11,60,41,60,72,60,96,60,123,60,154,60, +186,60,207,60,237,60,17,61,54,61,82,61,107,61,131,61,161,61,195,61,227,61,0,62,34,62,55,62,83,62,93,62, +116,62,141,62,169,62,196,62,217,62,244,62,9,63,42,63,69,63,96,63,127,63,151,63,173,63,193,63,214,63,230,63, +249,63,11,64,41,64,57,64,75,64,85,64,94,64,103,64,118,64,133,64,143,64,145,64,151,64,169,64,190,64,192,64, +194,64,201,64,210,64,228,64,236,64,241,64,253,64,12,65,14,65,22,65,30,65,32,65,43,65,45,65,47,65,49,65, +60,65,70,65,77,65,84,65,91,65,98,65,108,65,115,65,122,65,129,65,139,65,146,65,157,65,164,65,172,65,182,65, +190,65,198,65,206,65,214,65,225,65,233,65,241,65,249,65,1,66,12,66,20,66,28,66,36,66,46,66,59,66,70,66, +83,66,97,66,112,66,125,66,137,66,148,66,159,66,171,66,178,66,188,66,199,66,209,66,220,66,230,66,240,66,251,66, +3,67,10,67,16,67,22,67,24,67,26,67,28,67,30,67,32,67,38,67,40,67,45,67,56,67,69,67,82,67,94,67, +106,67,118,67,131,67,143,67,156,67,163,67,177,67,190,67,206,67,225,67,240,67,2,68,30,0,11,1,11,2,25,1, +14,3,9,1,32,53,3,2,1,32,60,3,3,1,19,15,12,1,8,9,2,1,32,42,16,4,1,15,17,28,0,9, +1,30,2,14,1,32,72,3,3,1,32,59,3,3,1,17,15,9,1,13,9,200,34,27,0,6,1,32,40,2,11,1, +32,69,3,198,28,65,1,14,15,7,1,17,9,2,1,32,41,16,196,58,65,17,25,0,196,32,6,2,196,91,14,3, +196,57,65,3,4,1,11,15,196,32,2,9,196,32,3,1,17,17,24,0,196,29,5,2,7,1,32,66,3,196,59,10, +15,6,1,20,9,3,1,32,40,16,196,57,65,17,22,0,196,29,4,2,6,1,32,65,3,198,58,7,15,196,29,2, +9,3,1,32,39,16,196,29,65,17,20,0,7,1,32,58,2,5,1,32,64,3,196,87,65,3,5,1,3,15,196,87, +4,9,200,34,19,0,6,1,32,61,2,5,1,32,63,3,4,1,32,61,3,12,1,25,9,3,1,32,38,16,4,1, +19,17,17,0,196,57,5,2,200,30,196,128,186,10,9,7,1,32,37,16,196,23,65,17,16,0,196,23,3,2,196,73, +2,3,196,51,2,3,5,1,20,9,11,1,198,50,65,17,15,0,196,77,10,2,198,100,196,97,17,9,15,1,198,46, +65,17,13,0,196,95,4,2,11,1,8,3,19,1,32,34,3,4,1,196,129,10,3,1,15,9,12,1,198,129,39,7, +17,12,0,5,1,32,66,2,32,45,1,29,3,3,1,32,58,3,5,1,15,9,9,1,32,46,16,4,1,23,17,11, +0,196,31,65,2,7,1,3,2,32,39,1,25,3,4,1,32,56,3,196,34,6,1,194,32,4,16,196,33,9,0,196, +110,7,2,4,1,28,2,10,1,23,3,3,1,32,55,3,5,1,13,9,196,128,166,14,16,196,63,65,17,8,0,196, +63,32,48,2,9,1,21,3,4,1,32,52,3,6,1,13,9,8,1,198,61,2,17,7,0,196,27,196,129,70,20,3, +3,1,32,51,3,196,54,10,1,32,49,16,196,52,2,17,6,0,196,52,9,2,5,1,19,3,3,1,32,50,3,5, +1,12,9,6,1,3,15,196,129,99,8,16,5,1,26,17,5,0,196,33,2,2,198,33,32,48,3,200,29,65,15,4, +1,32,47,16,196,57,65,17,3,0,196,25,4,2,3,1,196,57,196,128,233,10,1,10,3,196,59,5,1,6,15,4, +1,198,128,202,5,17,2,0,196,31,65,2,4,1,18,3,3,1,5,3,32,32,1,9,3,5,1,11,9,6,1,8, +15,196,129,189,5,16,196,67,2,17,65,0,196,37,2,2,198,37,4,3,8,1,17,3,6,1,198,36,196,66,4,15, +5,1,196,130,77,65,1,29,17,196,30,200,66,4,3,3,1,32,37,3,4,1,11,9,196,29,65,15,196,29,30,17, +196,128,254,32,108,2,4,1,17,3,196,28,6,3,196,27,196,129,215,2,15,5,1,196,5,198,130,75,15,17,2,1, +32,139,2,4,1,16,3,196,124,23,3,196,30,8,1,196,29,32,41,16,196,130,72,15,17,65,1,32,140,2,5,1, +196,28,20,3,196,128,140,5,1,4,15,7,1,198,55,65,17,32,142,2,6,1,14,3,196,129,3,17,3,5,1,196, +129,243,4,15,4,1,5,15,5,1,198,130,100,16,17,32,143,2,9,1,10,3,5,1,32,34,3,6,1,10,9,9, +1,11,15,6,1,32,40,16,3,1,32,34,17,32,145,2,14,1,3,3,196,128,233,14,3,8,1,11,9,4,1,65, +3,20,1,196,58,65,17,32,148,2,23,1,196,130,203,19,3,9,1,12,9,196,25,196,130,52,12,1,196,128,164,4, +17,32,151,2,32,36,1,3,3,11,1,3,2,198,51,4,3,12,1,32,45,16,196,129,17,6,17,32,156,2,32,42, +1,5,2,12,9,3,1,15,3,196,130,229,196,129,245,13,17,25,2,196,130,122,32,80,2,17,1,7,2,196,75,13, +3,198,25,65,17,21,2,16,1,32,164,2,196,16,65,3,196,131,12,3,16,196,129,170,11,17,20,2,22,1,32,158, +2,196,63,2,1,17,3,32,44,16,196,85,3,17,20,2,5,1,8,2,15,1,32,152,2,196,46,2,3,198,23,65, +17,32,38,2,15,1,32,133,2,4,1,9,2,196,21,65,3,32,43,16,196,43,2,17,32,43,2,14,1,32,125,2, +8,1,8,2,196,24,200,24,32,49,2,10,1,32,118,2,13,1,196,17,65,3,196,130,187,19,17,32,53,2,10,1, +32,111,2,12,1,11,2,196,59,198,22,65,17,32,56,2,196,131,242,32,75,2,196,20,3,2,13,9,196,128,184,5, +3,196,128,230,8,17,32,59,2,8,1,32,101,2,10,1,17,2,13,9,4,1,15,3,7,1,32,38,16,5,1,32, +43,17,32,62,2,196,102,32,88,2,196,107,12,2,196,29,12,3,11,1,32,36,16,6,1,32,44,17,32,64,2,196, +131,102,27,2,7,1,22,2,14,9,4,1,9,3,15,1,32,34,16,196,55,3,17,32,67,2,196,130,252,11,2,196, +28,65,2,196,28,7,3,10,1,5,9,4,1,32,32,16,196,30,65,17,32,69,2,196,131,55,19,2,196,30,196,30, +6,3,8,1,10,9,3,1,32,32,16,196,128,216,8,17,32,70,2,196,131,53,3,2,196,111,196,129,90,3,9,3, +1,196,130,112,13,9,3,1,30,16,196,59,2,17,32,72,2,6,1,32,75,2,10,1,26,2,15,9,4,1,65,3, +9,1,15,9,3,1,29,16,5,1,32,50,17,32,73,2,6,1,32,70,2,12,1,25,2,17,9,12,1,19,9,2, +1,28,16,5,1,32,51,17,32,75,2,196,132,40,196,26,2,2,18,9,11,1,21,9,2,1,27,16,196,26,65,17, +32,77,2,7,1,32,56,2,13,1,28,2,20,9,9,1,24,9,2,1,26,16,5,1,32,53,17,32,79,2,196,128, +245,12,2,30,1,30,2,196,50,196,132,68,196,132,174,2,9,2,1,25,16,196,32,65,17,32,81,2,9,1,29,2, +26,1,30,2,196,53,196,132,49,7,9,7,1,198,84,3,17,32,84,2,198,133,86,65,1,32,35,2,196,77,4,1, +19,9,10,1,196,24,65,17,32,86,2,196,132,146,11,2,26,9,7,1,14,9,12,1,198,128,179,7,17,32,166,2, +26,9,3,2,5,1,65,2,2,1,196,130,174,4,1,65,3,198,128,234,9,17,32,165,2,24,9,196,133,42,2,2, +2,1,196,90,4,1,6,3,196,49,2,17,32,171,2,10,9,10,2,6,1,3,2,3,1,5,9,9,1,7,3,3, +1,198,128,225,9,17,32,189,2,7,1,4,2,3,1,3,9,8,1,10,3,3,1,196,116,5,17,32,186,2,8,1, +6,2,3,1,2,9,7,1,12,3,3,1,198,128,217,9,17,32,183,2,9,1,8,2,3,1,65,9,196,131,73,3, +1,198,128,207,9,17,32,181,2,196,22,196,110,66,1,9,5,1,8,3,11,1,23,16,196,130,101,25,17,32,179,2, +196,130,97,4,2,198,107,11,1,20,16,196,128,247,12,17,32,175,2,10,1,14,2,8,1,196,40,2,1,198,21,65, +17,32,173,2,196,81,9,2,6,1,10,3,13,1,19,16,196,38,2,17,32,172,2,7,1,19,2,16,1,2,3,10, +1,21,16,4,1,32,69,17,32,170,2,6,1,22,2,196,21,6,3,3,1,198,21,65,17,32,169,2,5,1,23,2, +17,1,8,11,2,1,196,18,65,17,32,168,2,196,18,65,2,3,1,22,11,2,1,198,74,4,17,32,167,2,4,1, +26,2,196,18,196,104,6,17,32,166,2,198,14,65,11,196,25,2,17,32,165,2,196,42,2,2,3,1,21,11,3,1, +17,16,196,115,7,17,32,165,2,196,44,65,2,198,20,15,16,196,130,155,32,32,17,196,18,198,73,65,1,14,16,196, +15,65,17,32,166,2,3,1,196,73,3,1,13,16,196,45,4,17,196,16,25,2,4,1,17,11,7,1,198,17,65,17, +196,30,24,2,198,15,2,1,11,16,196,29,2,17,200,15,9,16,196,9,2,17,32,165,2,3,1,24,2,196,40,2, +11,10,1,5,16,196,71,7,17,196,20,23,2,196,17,65,11,196,17,196,34,2,17,196,34,22,2,15,1,196,31,7, +1,6,16,196,132,92,32,53,17,32,145,2,5,1,14,2,4,1,196,26,13,11,5,1,8,16,32,89,17,32,143,2, +6,1,15,2,3,1,196,21,4,11,65,1,7,16,32,91,17,196,132,193,16,2,3,1,22,2,3,1,6,13,23,11, +65,1,5,16,32,93,17,32,141,2,196,63,3,2,4,1,21,2,4,1,198,25,3,16,32,95,17,32,141,2,4,1, +18,2,200,45,66,13,1,22,11,65,1,3,17,196,129,75,22,17,32,140,2,196,24,196,129,41,196,43,7,13,65,1, +22,11,9,1,32,90,17,196,133,72,20,2,3,1,21,2,3,1,8,13,65,1,20,11,11,1,32,90,17,32,138,2, +196,67,2,2,198,25,65,1,196,23,19,11,13,1,32,89,17,32,137,2,196,107,2,2,196,25,4,1,9,13,65,1, +18,11,196,23,65,17,32,136,2,196,22,65,2,196,89,2,1,202,22,32,136,2,196,31,19,2,5,1,10,13,196,33, +14,1,32,89,17,32,135,2,197,1,74,196,50,2,13,198,18,32,134,2,198,13,65,13,13,1,6,11,196,29,32,134, +2,3,1,196,122,65,1,196,5,16,13,65,1,15,11,196,48,198,132,172,11,2,196,128,162,65,1,17,13,198,19,32, +132,2,200,77,7,13,196,28,65,11,196,127,32,131,2,196,124,196,128,212,6,1,18,13,198,19,32,131,2,196,40,18, +2,196,104,10,13,196,32,65,11,196,128,178,32,130,2,196,128,152,17,2,196,20,65,13,196,20,8,1,32,93,17,32, +129,2,200,20,22,11,32,98,17,32,129,2,198,108,18,2,4,1,22,13,196,17,32,128,2,196,61,196,12,65,13,21, +11,196,134,125,32,69,17,32,127,2,196,60,196,18,65,13,10,1,11,11,196,20,32,126,2,196,128,205,17,2,196,107, +6,13,9,11,3,1,8,11,196,40,32,125,2,196,129,100,65,2,198,129,105,196,59,20,11,196,57,32,124,2,196,18, +196,129,181,2,2,5,1,2,2,196,90,196,21,32,123,2,198,129,135,2,1,16,2,5,1,3,2,196,19,32,122,2, +202,17,2,2,4,1,19,13,22,11,196,106,32,121,2,195,1,129,61,5,1,7,2,4,1,16,13,24,11,196,125,32, +119,2,196,129,227,2,2,198,72,134,68,6,2,196,23,196,129,146,65,11,196,128,148,196,133,144,196,130,165,28,2,196, +80,15,2,6,1,11,2,4,1,15,13,198,29,32,45,2,28,1,32,44,2,198,129,236,134,115,13,2,7,1,13,2, +3,1,196,26,32,41,2,13,1,196,137,52,16,1,12,2,196,126,196,132,17,200,30,198,136,85,12,2,196,129,205,198, +129,228,196,130,96,10,17,32,38,2,11,1,18,2,30,1,16,2,6,1,16,2,7,1,14,2,6,1,17,2,3,1, +15,13,65,1,7,11,11,1,65,11,7,1,32,96,17,32,36,2,196,137,212,2,2,19,1,196,121,196,100,196,123,3, +2,196,131,139,198,39,196,129,233,196,32,32,34,2,196,132,209,196,116,196,128,225,196,128,244,198,101,5,2,196,64,65, +13,196,129,30,7,11,196,129,109,3,17,32,33,2,196,137,11,4,2,196,98,198,57,198,128,246,15,11,196,23,32,33, +2,196,136,191,32,41,2,196,114,65,2,198,79,196,136,66,21,13,14,11,196,47,32,33,2,196,129,39,32,53,2,196, +25,196,136,56,2,1,198,128,205,196,128,176,13,13,196,29,32,103,2,198,128,154,196,19,14,2,2,1,22,13,65,1, +13,11,196,93,32,100,2,196,132,17,196,134,194,196,129,107,196,131,88,196,71,66,13,1,196,130,63,8,17,32,98,2, +196,134,87,11,2,196,133,208,196,92,196,130,64,3,1,196,129,62,32,96,2,196,134,108,196,132,232,196,137,57,198,128, +164,5,13,196,130,166,196,129,85,32,96,2,196,131,247,196,137,79,198,130,231,12,2,7,1,17,13,196,130,190,67,11, +1,16,32,97,17,32,96,2,196,129,182,198,131,2,196,136,227,11,2,3,1,65,2,196,130,190,6,13,196,34,196,131, +156,7,17,32,130,2,196,129,212,14,2,4,1,10,2,3,1,65,2,7,1,14,13,196,61,196,131,232,9,17,32,128, +2,196,132,76,3,2,65,9,4,1,8,2,4,1,4,2,196,130,230,196,131,247,3,16,7,1,32,89,17,32,127,2, +196,32,65,2,2,9,196,27,2,2,196,5,196,131,93,2,13,5,1,19,11,4,16,7,1,32,88,17,32,126,2,196, +32,65,2,2,9,5,1,4,2,196,66,2,1,9,13,196,132,51,2,1,6,16,196,132,50,3,17,32,124,2,196,132, +244,10,2,3,9,4,1,198,133,230,7,2,198,131,157,2,1,6,11,5,1,9,11,196,38,2,16,196,132,106,3,17, +32,123,2,196,69,4,2,3,9,4,1,65,2,5,1,9,2,196,38,12,1,8,11,2,1,198,132,138,2,17,32,122, +2,5,1,32,36,2,4,9,8,1,10,2,3,1,65,13,22,1,8,11,2,1,10,16,5,1,32,84,17,32,120,2, +196,99,5,2,5,9,196,130,100,3,1,65,11,11,1,18,11,3,1,198,132,204,2,17,32,119,2,196,59,196,89,198, +128,188,6,2,196,28,9,1,20,11,3,1,12,16,196,133,16,4,17,32,117,2,196,139,222,65,2,6,9,196,129,19, +198,133,114,8,11,9,1,198,138,62,32,54,17,32,115,2,196,135,178,20,2,196,27,196,133,135,196,24,9,16,196,133, +49,32,111,2,196,134,49,196,134,115,32,32,2,6,9,196,136,135,196,133,162,196,130,114,9,16,196,133,216,10,17,32, +107,2,196,140,105,196,133,142,196,31,196,134,61,7,2,198,30,65,16,196,133,241,9,17,32,103,2,12,1,16,2,5, +1,30,2,8,9,5,1,6,2,2,1,29,11,12,1,11,16,4,1,32,77,17,32,84,2,198,131,71,196,130,211,2, +2,196,134,23,8,2,196,36,5,2,196,34,9,1,15,16,196,134,46,7,17,32,84,2,23,1,26,2,196,134,125,4, +2,2,1,8,2,9,9,196,129,99,196,66,6,1,19,16,196,134,78,6,17,32,84,2,18,1,32,33,2,24,1,9, +2,9,9,6,1,65,2,3,1,31,11,3,1,21,16,3,1,32,75,17,32,82,2,6,1,4,7,32,45,2,22,1, +10,2,9,9,9,1,19,11,8,1,4,11,3,1,6,16,7,1,8,16,4,1,32,74,17,32,32,2,196,139,154,16, +2,7,1,5,7,32,50,2,15,1,12,2,196,138,242,198,131,78,9,1,4,11,4,1,4,16,9,1,196,43,32,73, +17,32,32,2,196,131,83,3,2,10,1,6,7,32,78,2,196,139,22,65,1,5,11,18,1,8,11,3,1,196,38,196, +129,8,3,17,32,32,2,196,64,196,135,153,8,1,9,7,196,134,56,3,2,196,130,112,17,2,12,9,7,1,3,11, +6,1,65,11,6,1,13,11,200,46,65,0,32,35,2,17,1,196,131,157,6,1,12,7,197,1,42,20,2,13,9,7, +1,27,11,3,1,5,16,7,1,11,16,196,135,41,2,17,196,39,196,132,10,11,1,17,7,196,74,196,129,251,4,2, +3,1,196,137,171,65,9,7,1,25,11,196,128,225,3,16,196,135,78,65,17,2,0,32,38,2,4,1,3,7,19,1, +26,7,196,132,176,12,2,196,130,8,196,139,193,196,130,73,198,119,19,11,196,128,191,20,16,196,42,2,0,32,37,2, +196,39,32,46,7,196,128,150,198,129,106,5,2,196,130,169,136,129,16,196,119,20,11,196,36,196,129,169,3,0,196,135, +112,31,2,196,36,65,7,196,128,184,196,132,44,13,2,196,132,221,17,9,7,1,18,11,196,110,3,16,196,139,53,32, +35,17,4,0,196,140,194,196,130,157,7,7,16,1,27,7,196,140,206,196,132,85,10,2,196,130,172,18,9,7,1,16, +11,196,42,196,138,144,28,17,4,0,5,1,27,2,5,1,3,7,24,1,24,7,5,1,32,32,2,7,1,25,2,6, +1,11,2,19,9,6,1,15,11,3,1,28,16,3,1,32,68,17,5,0,196,135,232,196,130,237,65,7,29,1,23,7, +196,131,213,196,134,140,3,1,13,2,20,9,196,129,41,3,1,65,16,196,130,8,3,16,196,138,229,27,17,6,0,196, +136,36,14,1,17,2,7,1,22,7,196,141,72,7,2,196,136,144,2,2,196,136,193,5,2,196,141,136,3,9,7,1, +11,11,200,52,65,16,196,139,19,26,17,7,0,196,133,20,2,2,196,130,120,196,141,117,2,1,198,51,196,141,154,20, +1,15,2,196,142,187,196,129,66,9,11,196,42,65,16,196,140,8,32,32,17,8,0,7,1,14,2,12,1,28,2,5, +1,22,7,4,1,21,2,2,1,14,2,22,1,17,2,10,1,18,9,8,1,7,11,2,1,2,16,7,1,21,16,4, +1,32,65,17,10,0,28,1,32,32,2,5,1,21,7,4,1,20,2,3,1,19,2,12,1,21,2,4,1,2,11,8, +1,17,9,10,1,3,11,2,1,30,16,4,1,32,65,17,12,0,25,1,32,34,2,198,45,196,133,226,196,135,103,31, +2,5,1,4,11,196,138,166,65,9,196,137,31,12,16,196,139,188,24,17,16,0,196,130,191,6,2,3,1,10,7,10, +1,2,2,196,134,33,196,129,159,15,2,196,43,196,131,195,196,138,159,196,136,42,196,136,145,9,16,196,140,180,30,17, +198,143,138,196,129,234,2,7,14,1,196,134,168,198,131,73,196,143,93,196,121,196,130,204,196,136,154,196,138,202,65,1, +3,16,196,45,196,140,17,23,17,26,0,196,130,49,3,2,3,1,65,7,18,1,4,2,21,1,32,51,2,4,1,8, +11,3,1,4,11,11,1,196,128,167,66,1,9,196,88,196,129,3,29,17,26,0,198,130,251,196,134,234,198,138,9,32, +42,2,196,38,65,11,196,38,196,130,171,198,143,227,4,1,196,38,65,16,196,140,97,22,17,25,0,4,1,32,44,2, +9,1,26,2,10,1,8,2,9,1,32,34,2,5,1,10,11,3,1,11,11,14,1,19,9,2,1,22,16,3,1,32, +62,17,24,0,196,129,155,7,2,196,134,120,65,2,9,1,5,2,198,129,88,3,1,198,41,5,11,14,1,14,9,198, +39,196,142,29,32,32,17,196,38,6,2,196,131,72,2,2,196,138,147,196,40,198,137,0,196,76,2,11,198,137,96,5, +1,6,9,196,130,176,196,141,159,27,17,24,0,196,131,62,198,135,23,31,2,198,135,51,196,134,114,9,1,15,11,196, +138,15,3,11,19,1,24,16,196,140,245,20,17,196,35,13,2,196,128,151,5,2,196,135,84,65,2,11,1,196,139,13, +2,1,18,11,196,132,147,196,131,29,2,16,196,141,229,26,17,196,37,65,2,196,139,127,17,2,198,137,123,198,135,171, +196,130,124,3,11,196,71,5,11,198,144,103,32,41,17,18,0,5,1,65,0,196,134,189,196,135,156,24,1,196,128,167, +14,11,196,26,9,0,18,1,32,97,2,6,1,16,2,14,1,2,3,21,1,3,3,12,1,19,11,4,1,32,37,16, +4,1,32,58,17,6,0,21,1,32,99,2,5,1,15,2,11,1,10,3,16,1,3,3,32,35,1,32,38,16,3,1, +32,58,17,3,0,196,129,34,196,133,34,32,68,2,196,137,91,196,142,158,5,3,7,1,6,3,32,34,1,32,36,16, +196,35,2,0,196,141,38,26,2,196,137,97,196,142,181,196,28,4,1,10,3,196,140,240,196,142,45,21,17,65,0,196, +144,1,8,2,196,137,124,196,55,32,41,3,196,141,7,5,16,196,142,170,23,17,196,135,118,32,57,2,196,137,148,196, +134,21,196,145,153,196,144,168,32,35,17,3,1,32,128,2,4,1,22,2,5,1,32,59,3,2,1,32,38,16,3,1, +32,56,17,196,25,65,2,198,24,32,58,3,198,65,22,17,196,137,128,196,136,178,32,58,3,196,145,33,196,142,245,21, +17,32,104,2,196,132,35,196,138,114,198,143,225,32,39,3,198,24,32,104,2,196,132,166,196,73,196,17,198,68,196,143, +186,25,17,32,104,2,196,139,62,196,90,196,144,0,4,3,196,144,91,11,3,196,93,196,131,43,20,17,32,105,2,196, +43,196,144,108,2,3,14,1,23,3,198,145,168,32,35,17,32,105,2,196,139,107,198,138,169,65,1,21,3,32,42,1, +32,35,16,196,143,92,19,17,32,106,2,196,139,131,196,138,107,196,25,3,1,4,12,32,35,1,32,35,16,196,142,170, +12,17,196,27,198,138,215,21,3,3,1,12,12,22,1,2,12,196,142,7,196,132,91,18,17,32,107,2,196,50,198,24, +4,12,196,26,6,12,196,142,1,196,22,196,144,41,26,2,198,139,27,20,3,4,1,16,12,198,24,4,16,196,143,182, +17,17,196,24,65,2,198,138,219,20,3,196,67,5,12,196,46,196,65,17,17,32,109,2,196,140,27,200,22,65,12,2, +1,19,12,196,142,62,3,16,3,1,65,16,32,50,17,32,110,2,196,139,186,196,138,244,19,3,196,70,2,12,200,32, +196,131,244,16,17,32,110,2,196,140,75,196,137,217,5,2,196,145,3,19,12,196,55,65,12,200,55,32,49,17,32,111, +2,196,129,82,3,2,204,28,198,70,196,144,198,20,17,198,18,196,137,255,196,145,41,196,143,149,196,65,196,96,65,1, +19,12,196,128,178,15,17,32,112,2,196,129,123,2,2,196,31,196,143,237,65,3,196,128,181,2,12,196,6,200,123,32, +48,17,32,113,2,196,140,45,196,56,196,143,55,198,24,196,128,223,14,17,32,113,2,196,129,168,65,2,198,21,196,104, +32,47,17,32,114,2,196,139,241,65,2,196,138,89,196,141,186,196,138,80,14,3,196,128,130,198,94,196,129,13,2,16, +196,143,205,7,17,32,114,2,196,131,69,196,140,253,196,141,218,13,3,196,94,198,122,3,12,198,128,215,32,47,17,32, +115,2,196,134,83,16,2,3,1,202,28,196,128,144,32,34,16,196,143,254,6,17,32,115,2,196,131,118,3,1,4,2, +2,1,200,50,196,26,196,132,218,12,17,32,116,2,196,140,182,196,141,67,14,2,2,1,5,2,196,145,203,7,3,196, +128,170,196,128,170,196,107,3,1,65,16,32,45,17,200,36,7,2,3,1,11,3,196,128,193,3,12,196,6,196,143,120, +2,16,196,144,79,5,17,32,117,2,196,140,242,198,132,223,200,29,196,143,168,196,131,76,11,17,196,20,196,76,6,2, +3,1,196,130,241,11,12,3,1,12,12,196,143,222,196,144,123,4,17,196,141,124,32,112,2,198,108,7,2,2,1,10, +3,3,1,196,129,40,32,51,1,32,34,16,196,145,101,10,17,196,31,196,128,235,9,2,198,140,141,9,3,3,1,2, +12,32,53,1,198,28,196,52,196,77,196,137,157,10,2,3,1,198,23,196,79,20,1,16,12,196,144,15,65,16,196,144, +203,3,17,196,146,122,196,129,69,200,50,23,12,196,129,153,196,130,13,196,131,202,9,17,196,134,155,32,83,2,196,141, +134,196,74,196,129,175,6,12,196,6,196,50,196,145,197,9,17,198,26,198,140,232,8,3,196,128,195,3,12,196,27,196, +129,21,196,145,219,8,17,196,138,167,32,45,2,197,1,129,105,2,1,206,30,196,129,52,32,41,2,65,9,196,116,2, +2,196,21,200,46,65,12,196,128,214,7,17,198,24,2,9,198,139,238,20,2,196,130,11,65,2,196,129,164,2,1,196, +29,196,129,87,196,145,81,2,1,13,2,196,129,106,27,2,4,9,196,142,188,196,129,173,6,2,196,142,2,2,1,7, +3,196,130,72,8,12,196,111,196,129,101,6,17,200,39,136,136,79,2,9,196,134,104,196,67,198,32,196,128,132,196,129, +170,196,130,190,2,16,196,145,189,65,17,196,69,196,139,45,2,2,6,9,196,139,155,196,130,109,196,142,66,65,2,196, +67,200,39,196,134,119,5,17,196,139,241,198,31,196,139,188,200,26,196,56,6,12,196,128,254,65,16,196,145,242,196,140, +7,196,53,136,136,163,4,9,196,138,43,196,138,69,17,2,2,1,6,3,196,128,221,196,130,95,9,12,198,130,218,32, +38,17,196,140,43,196,128,174,32,49,2,7,9,196,138,80,196,143,252,26,2,202,38,196,66,196,146,132,2,17,18,2, +196,148,128,4,2,8,9,196,138,106,196,130,202,3,1,200,62,196,130,39,65,16,196,146,161,65,17,19,2,198,30,196, +135,133,196,88,65,2,204,26,198,146,91,32,62,2,10,9,196,143,134,6,2,196,143,88,196,147,233,196,129,68,196,130, +6,7,12,196,131,147,2,17,21,2,196,139,253,196,147,243,25,2,196,30,198,130,225,9,12,196,129,98,5,12,196,131, +92,196,135,53,65,17,22,2,196,140,27,12,9,196,143,178,196,143,104,196,148,36,206,35,24,2,196,149,55,2,2,13, +9,197,1,132,147,5,2,3,1,196,130,109,198,54,196,128,217,65,16,196,148,46,5,17,196,147,8,14,9,66,1,9, +21,2,196,136,25,3,1,6,3,196,129,73,196,111,2,12,196,83,27,2,196,145,87,196,138,78,11,9,19,2,196,143, +182,4,1,196,128,242,198,132,7,14,12,196,131,201,196,62,4,17,29,2,196,145,119,31,2,18,9,32,48,2,196,88, +10,12,196,128,138,65,12,196,90,65,16,196,148,24,65,17,198,137,144,196,145,221,65,9,196,139,211,14,2,4,1,196, +146,91,22,1,65,12,3,1,4,12,19,1,10,12,196,132,12,198,41,196,134,170,196,141,125,196,137,56,4,9,198,131, +61,7,3,198,130,163,2,1,196,29,196,128,154,3,17,32,37,2,196,146,61,30,2,16,9,198,137,95,65,1,7,3, +196,130,192,32,49,1,15,12,196,126,2,17,196,150,242,32,52,2,17,9,196,139,116,196,145,99,198,130,101,196,132,113, +16,12,196,27,196,135,8,196,145,209,15,2,196,27,196,149,37,198,24,196,133,168,196,150,234,14,17,32,47,2,196,138, +195,196,143,57,196,145,167,15,9,196,129,157,198,130,151,6,12,196,128,176,196,131,218,2,16,196,149,52,65,17,32,50, +2,196,147,127,198,135,25,17,2,196,149,22,4,9,196,140,195,198,38,3,12,196,67,13,17,32,54,2,27,1,24,2, +196,150,46,31,2,196,131,23,30,12,196,128,233,198,27,196,134,187,196,151,61,196,146,142,196,150,70,29,2,196,147,84, +197,1,129,0,3,12,196,134,27,196,149,132,196,138,88,196,145,123,196,140,205,4,2,196,150,43,196,130,137,8,2,196, +59,196,132,45,196,129,60,2,16,196,151,125,12,17,196,130,250,22,2,196,150,71,65,9,196,145,110,3,2,196,147,148, +196,129,63,202,35,196,24,65,2,196,51,4,2,3,1,10,3,196,129,80,196,132,96,14,12,196,134,223,196,151,176,11, +17,196,130,181,32,37,2,198,53,2,2,10,3,196,130,237,7,12,196,129,113,196,129,250,196,82,11,17,65,3,196,30, +65,2,196,80,196,137,88,196,31,196,130,22,196,129,16,196,128,189,11,17,2,3,198,25,65,9,196,144,71,12,3,196, +129,158,196,131,136,11,12,196,79,10,17,2,3,196,48,196,48,65,9,196,143,103,200,26,196,130,136,196,128,149,10,17, +3,3,198,22,196,145,5,196,132,215,15,12,196,131,202,12,12,196,134,230,196,150,201,4,3,196,131,45,31,2,2,1, +196,137,172,15,9,196,140,249,4,2,14,3,198,78,198,129,234,65,16,196,152,71,9,17,5,3,196,131,78,28,2,198, +143,109,2,1,15,9,6,1,10,2,15,3,196,132,10,10,12,196,70,198,135,153,25,17,6,3,196,148,68,198,147,13, +6,2,196,128,240,6,1,8,2,15,3,196,130,42,196,130,73,196,131,145,4,16,196,152,138,8,17,6,3,196,148,132, +4,2,196,136,45,196,147,116,6,1,6,2,206,36,6,3,196,137,107,32,39,2,196,142,208,7,1,196,139,127,3,2, +16,3,196,56,198,131,145,3,16,196,151,145,6,3,4,2,196,140,9,32,64,2,196,146,192,7,1,13,9,6,1,2, +2,15,3,196,129,23,196,130,187,7,12,196,152,115,196,152,226,7,17,6,3,5,2,196,138,150,32,57,2,196,148,223, +196,151,180,196,147,94,3,3,200,38,196,152,16,196,27,196,140,41,196,148,134,196,143,222,12,2,196,128,153,196,128,247, +196,131,202,10,12,196,136,75,196,128,216,6,17,196,59,32,84,2,196,149,73,9,2,196,127,65,1,196,129,141,196,130, +98,5,12,198,31,196,83,196,140,180,196,54,7,2,196,150,150,65,9,10,1,5,3,196,129,139,196,131,21,8,12,200, +152,231,3,17,196,114,32,80,2,196,139,79,14,2,196,128,237,3,1,3,3,196,135,99,11,1,23,12,196,131,27,8, +12,196,153,65,196,153,120,5,17,5,3,196,132,193,196,140,10,5,2,196,70,2,9,12,1,196,131,18,6,1,9,12, +198,135,120,196,139,81,21,16,196,37,196,34,196,129,48,196,140,57,65,1,17,9,9,1,196,135,164,32,45,1,198,153, +124,3,17,196,59,8,2,196,153,180,196,152,217,2,9,198,151,220,32,58,1,65,9,196,130,31,4,17,196,84,7,2, +196,153,205,2,2,196,153,64,196,136,216,9,3,196,136,254,27,3,4,9,5,1,32,36,16,196,153,102,6,3,196,133, +52,5,2,196,144,234,65,2,196,154,1,196,152,24,32,55,3,5,9,196,32,196,129,197,3,17,196,31,3,2,196,153, +239,3,2,198,154,3,196,153,243,2,3,3,1,6,9,196,131,61,3,17,6,3,196,133,42,196,55,196,129,252,5,9, +196,153,26,32,40,3,4,1,7,9,196,131,88,2,17,7,3,196,144,113,196,82,196,148,230,22,9,196,152,104,32,41, +3,7,1,7,9,3,1,12,16,4,1,21,16,196,154,40,8,3,4,1,32,66,2,6,1,29,2,14,1,24,2,6, +1,23,9,8,1,65,3,32,33,1,21,3,6,1,9,9,3,1,3,16,14,1,21,16,3,1,18,17,196,45,196,138, +10,12,2,198,140,154,196,141,223,8,2,3,1,65,3,196,49,65,9,32,95,1,21,16,196,154,143,9,3,196,128,187, +6,2,196,131,43,23,1,20,2,3,1,2,3,196,154,112,65,9,6,1,22,14,32,56,1,6,14,196,142,81,196,130, +142,10,3,196,128,198,196,140,179,196,140,128,4,1,196,150,142,10,9,3,1,32,36,14,18,1,32,32,14,198,142,115, +65,1,16,17,11,3,198,154,215,23,2,196,138,225,196,148,192,8,3,196,140,21,3,9,2,1,32,87,14,196,142,147, +65,16,3,1,16,17,13,3,196,155,21,65,2,196,149,3,196,150,83,8,2,196,138,5,10,1,16,9,202,37,65,1, +15,17,14,3,196,143,117,198,138,212,9,2,196,29,196,136,1,65,3,11,1,11,9,196,101,32,52,14,196,139,100,65, +16,3,1,15,17,16,3,196,56,196,145,203,65,2,196,63,4,2,196,153,48,16,1,4,9,3,1,26,14,11,1,32, +51,14,198,39,65,1,14,17,18,3,196,150,173,7,2,196,149,121,198,39,198,153,203,196,152,169,196,128,201,5,1,9, +14,196,5,32,38,14,196,128,138,65,16,3,1,14,17,21,3,196,150,212,65,2,196,151,205,196,143,234,196,77,196,151, +207,2,1,20,14,3,1,17,14,4,1,32,44,14,200,41,23,3,196,151,93,196,149,210,196,142,15,16,2,198,153,72, +65,1,32,42,14,4,1,32,42,14,198,69,65,1,13,17,26,3,9,1,2,3,18,2,10,1,23,2,5,1,32,49, +2,5,1,32,39,3,3,1,32,44,14,3,1,32,41,14,3,1,24,16,3,1,13,17,29,3,32,33,1,26,2,196, +136,190,7,2,196,36,65,3,196,35,65,14,4,1,32,39,14,198,34,65,1,12,17,30,3,31,1,32,80,2,196,153, +139,8,3,196,127,196,28,9,1,17,14,196,129,89,2,14,196,65,196,129,51,12,17,196,85,10,1,198,130,96,196,128, +239,25,3,3,1,16,14,19,1,14,14,196,128,171,18,14,196,33,65,1,11,17,32,58,3,196,130,124,196,132,226,32, +33,3,3,1,15,14,196,128,234,7,1,198,35,196,151,188,3,1,11,17,196,156,203,32,76,2,196,154,84,3,3,2, +1,14,14,4,1,196,5,2,1,13,14,196,128,235,16,14,196,142,94,2,16,196,38,196,156,238,196,130,29,10,2,196, +38,65,3,3,1,12,14,196,41,5,14,5,1,196,37,15,14,10,1,19,16,4,1,10,17,196,35,9,2,196,31,196, +31,11,14,196,129,35,65,14,196,129,76,3,14,196,129,117,8,14,11,1,19,16,3,1,10,17,196,157,82,196,130,96, +198,128,223,6,3,3,1,11,14,134,106,22,14,4,1,198,75,15,14,3,1,4,5,4,1,196,71,9,17,32,61,3, +196,156,32,196,129,0,196,128,230,20,14,4,1,11,14,196,129,183,7,14,3,1,5,5,4,1,196,71,9,17,32,62, +3,196,130,133,3,2,196,131,95,21,3,2,1,10,14,196,71,4,14,196,41,196,115,12,14,3,1,6,5,196,150,224, +2,16,3,1,9,17,196,156,250,196,140,213,8,2,196,155,123,32,33,3,2,1,9,14,196,130,167,3,14,4,1,10, +14,196,130,9,6,14,200,46,196,130,105,8,17,32,66,3,196,129,114,8,2,196,132,147,32,37,3,196,40,196,129,87, +6,14,196,38,196,128,194,11,14,198,36,32,66,3,196,141,62,196,156,87,32,35,3,196,68,13,1,20,14,4,1,9, +14,196,130,73,5,14,3,1,7,5,198,110,8,17,32,67,3,198,129,214,196,156,170,2,3,196,128,143,15,1,18,14, +196,36,196,129,0,10,14,200,34,32,68,3,196,146,55,2,2,196,132,5,198,128,172,196,129,145,65,14,4,1,8,14, +196,93,2,1,7,5,196,128,170,65,16,3,1,7,17,32,69,3,196,143,248,196,147,161,196,157,169,65,3,196,69,9, +1,11,14,3,1,208,40,32,71,3,196,142,75,65,2,13,1,32,66,3,196,24,5,1,7,14,3,1,7,14,206,63, +32,73,3,196,146,41,2,2,196,140,99,32,45,3,196,21,3,14,32,33,1,3,14,196,30,204,90,65,1,6,17,32, +76,3,196,155,127,32,70,3,198,27,65,1,2,14,4,1,6,14,196,115,65,1,198,129,28,196,129,26,6,17,32,80, +3,27,1,32,78,3,200,57,65,14,3,1,204,29,32,87,3,15,1,32,83,3,200,17,32,185,3,196,129,151,31,1, +196,128,221,200,52,196,140,32,5,17,32,185,3,2,1,12,14,5,1,4,14,22,1,6,14,3,1,5,14,196,129,105, +2,1,198,129,104,196,33,214,30,65,1,5,5,198,131,177,5,17,202,41,21,1,7,14,200,32,200,18,202,51,7,1, +3,14,11,1,7,14,196,46,196,129,154,196,53,5,5,198,131,74,4,17,198,25,196,129,124,196,128,192,196,117,4,14, +196,129,172,196,124,198,25,198,44,6,14,4,1,8,14,3,1,200,20,3,3,196,158,120,32,116,3,196,130,125,66,14, +1,5,14,7,1,11,14,2,1,196,29,4,14,196,129,223,7,14,3,1,4,5,196,130,231,3,17,3,4,23,1,32, +159,3,196,128,138,11,1,4,14,196,39,10,14,3,1,3,14,196,129,253,2,14,200,37,196,30,12,1,32,147,3,3, +1,196,65,196,5,21,14,2,1,198,63,65,14,200,60,18,4,28,1,32,139,3,3,1,6,14,9,1,198,92,10,14, +2,1,198,52,196,26,31,4,24,1,32,130,3,3,1,7,14,7,1,6,14,196,132,196,2,1,3,14,196,112,2,14, +3,1,5,5,27,16,3,1,3,17,32,41,4,26,1,32,118,3,196,128,186,3,1,66,14,1,196,130,92,196,131,249, +4,14,196,67,196,130,74,198,145,159,2,17,32,49,4,30,1,32,106,3,196,129,155,196,129,108,65,14,196,69,196,131, +181,198,132,33,2,14,196,131,124,196,131,192,196,38,32,60,4,196,160,84,32,87,3,196,130,173,3,1,198,35,196,132, +62,200,106,2,14,8,1,28,16,3,1,2,17,32,73,4,16,1,32,97,3,196,130,203,196,128,251,196,130,161,196,131, +88,3,1,196,64,196,129,158,32,33,16,4,1,65,17,32,80,4,196,129,162,8,3,198,132,119,196,132,118,2,14,2, +1,196,129,70,32,36,14,196,140,139,3,1,65,17,32,85,4,196,160,143,15,3,196,32,65,14,2,1,196,128,131,32, +39,14,198,28,32,90,4,196,160,165,10,3,196,131,138,65,14,196,128,183,196,132,10,8,14,196,51,65,1,32,96,4, +12,1,32,78,3,196,26,196,75,15,14,196,132,100,196,82,32,36,16,3,1,32,100,4,12,1,32,74,3,196,132,51, +196,128,189,11,14,198,142,35,32,104,4,196,160,206,2,3,196,132,70,196,129,2,196,115,2,16,2,1,32,108,4,13, +1,32,65,3,65,1,32,39,14,65,1,4,14,16,1,32,32,14,3,1,32,36,16,2,1,32,112,4,196,142,247,32, +38,3,32,40,14,22,1,30,14,198,24,32,116,4,196,135,2,23,3,196,129,199,65,14,12,1,7,15,196,129,180,8, +14,196,143,182,65,1,32,121,4,196,143,195,196,131,112,5,14,7,1,15,15,198,129,91,8,14,3,1,8,16,196,144, +152,65,1,32,127,4,196,161,126,14,1,20,14,196,161,130,196,159,148,27,14,4,1,65,3,196,131,218,66,1,10,196, +34,32,129,4,32,59,3,24,1,8,14,5,1,12,15,4,1,7,15,3,1,27,14,3,1,65,3,7,1,65,10,4, +1,25,16,65,1,32,130,4,32,69,3,27,1,9,15,6,1,6,15,14,1,17,14,10,1,3,10,196,144,245,196,26, +196,157,92,11,1,196,53,198,159,4,22,1,3,14,196,24,2,10,196,145,12,32,114,4,18,1,32,86,3,12,1,3, +15,5,1,7,15,32,38,1,9,10,3,1,25,16,32,111,4,22,1,32,84,3,5,1,2,3,13,1,7,15,32,36, +1,12,10,3,1,65,16,5,1,19,16,32,108,4,11,1,8,4,196,135,173,6,3,9,1,65,3,6,1,2,3,196, +128,232,32,34,1,15,10,10,1,18,16,32,107,4,7,1,16,4,196,161,162,196,33,196,29,196,161,86,2,10,196,159, +123,65,15,15,1,32,36,10,196,83,65,3,196,155,143,32,105,4,6,1,21,4,196,161,198,3,3,16,1,4,3,5, +1,2,10,3,1,6,15,6,1,7,10,3,1,32,36,10,3,1,5,3,196,155,183,32,103,4,196,77,7,4,13,1, +32,57,3,6,1,196,160,19,196,140,249,2,1,196,133,141,65,10,3,1,196,159,207,8,10,4,1,32,37,10,196,23, +196,155,198,2,16,32,102,4,196,87,5,4,32,46,1,22,3,196,129,3,196,157,17,200,45,5,15,4,1,9,10,202, +39,32,100,4,196,29,196,131,47,20,1,12,3,198,160,211,6,3,2,1,196,136,108,196,72,4,15,3,1,11,10,196, +71,196,160,210,196,155,251,3,16,32,99,4,196,69,19,4,32,45,1,6,3,196,129,21,4,1,5,3,3,1,210,43, +32,98,4,5,1,32,82,4,21,1,7,15,198,21,3,3,6,1,3,15,196,90,3,10,196,128,174,65,10,198,61,32, +96,4,196,55,32,44,4,14,1,8,15,4,1,197,1,160,226,10,1,12,10,196,30,11,1,16,16,32,95,4,196,60, +14,4,9,1,196,28,4,3,2,1,196,81,65,10,8,1,13,10,196,59,8,1,19,16,32,94,4,196,30,5,4,196, +107,65,1,200,30,65,10,6,1,14,10,196,84,196,135,30,32,93,4,196,25,65,4,196,25,196,134,157,196,146,41,19, +10,196,23,32,92,4,196,20,2,4,198,129,252,2,1,196,23,22,1,32,37,10,196,135,71,32,91,4,196,23,65,4, +196,129,99,6,1,3,3,134,4,3,3,4,1,2,10,24,1,196,31,32,90,4,5,1,32,51,4,32,35,1,20,4, +196,161,26,196,157,124,196,28,6,10,17,1,32,38,10,196,151,36,2,16,32,89,4,5,1,32,49,4,32,64,1,65, +15,9,1,8,3,4,1,23,10,3,1,32,38,10,7,1,20,16,32,88,4,4,1,32,50,4,32,72,1,65,3,2, +1,7,3,196,31,65,10,196,31,3,1,65,3,4,1,19,16,196,32,32,49,4,4,1,32,41,5,27,1,2,3,196, +162,16,5,1,24,10,196,62,2,1,3,3,196,134,144,32,87,4,196,62,196,134,47,32,54,5,7,1,4,3,196,23, +196,128,218,10,10,196,129,173,65,10,196,35,196,151,55,14,16,32,86,4,196,96,196,65,21,5,7,1,3,3,2,1, +196,130,40,198,64,134,11,65,3,196,30,32,85,4,196,128,152,65,4,196,64,3,5,7,1,2,3,196,129,241,2,1, +27,10,198,124,2,3,196,59,32,85,4,198,56,196,135,57,200,128,234,25,10,196,81,3,1,196,112,32,84,4,196,128, +170,198,49,196,135,46,4,3,4,1,2,10,196,129,124,2,10,196,128,144,14,10,196,32,32,83,4,196,128,199,65,4, +196,107,8,5,198,161,176,5,1,8,10,196,128,170,13,10,4,1,196,79,65,16,32,82,4,196,31,198,107,7,5,6, +1,196,161,227,5,1,5,10,196,84,3,3,196,128,224,196,26,196,50,196,135,159,198,162,187,202,22,32,81,4,198,38, +5,5,7,1,14,15,7,1,5,10,198,129,34,196,129,1,32,81,4,3,1,32,52,4,196,89,9,5,196,165,16,196, +60,198,129,237,4,16,32,80,4,196,44,4,5,198,165,66,65,1,32,32,10,196,150,27,6,16,32,80,4,200,43,2, +5,196,165,53,196,131,4,28,1,196,130,233,196,150,228,32,79,4,196,128,152,198,128,152,12,5,196,165,111,4,15,32, +44,1,29,16,32,79,4,196,43,2,5,4,1,32,34,15,28,1,2,5,196,160,150,65,16,32,78,4,196,84,196,136, +9,31,15,6,1,21,5,3,1,2,5,196,151,35,32,78,4,196,43,3,5,4,1,26,15,10,1,26,5,196,151,53, +32,77,4,196,123,196,128,201,18,15,14,1,28,5,196,151,70,32,77,4,196,36,2,5,7,1,12,15,196,19,4,5, +196,151,88,32,77,4,3,1,32,51,4,196,128,180,11,5,14,1,3,15,12,1,32,36,5,196,160,251,32,76,4,196, +44,3,5,25,1,32,39,5,196,151,209,32,76,4,200,41,3,5,21,1,32,42,5,196,151,224,32,75,4,198,129,170, +32,34,5,17,1,32,44,5,196,151,241,32,75,4,196,33,6,5,13,1,32,45,5,196,152,0,32,74,4,196,33,10, +5,196,128,159,25,5,196,152,15,32,74,4,196,103,196,129,188,32,95,5,196,152,29,32,73,4,196,30,32,53,5,196, +152,40,32,73,4,3,1,32,50,4,4,1,32,160,5,3,1,27,16,32,72,4,198,130,37,32,101,5,196,161,101,32, +72,4,200,30,2,5,196,161,111,32,71,4,4,1,198,130,92,32,122,5,196,161,125,32,70,4,196,37,196,25,32,70, +4,198,22,65,5,196,149,191,32,69,4,196,11,196,11,32,69,4,198,78,196,116,32,113,5,196,149,211,32,68,4,196, +130,171,16,1,32,153,5,196,149,225,32,68,4,196,129,155,5,4,16,1,32,147,5,196,149,241,32,67,4,196,130,3, +12,4,15,1,32,142,5,196,150,1,32,67,4,196,33,196,16,2,1,32,135,5,196,150,16,32,66,4,196,32,11,4, +196,128,222,32,86,5,196,150,32,196,131,254,15,4,196,66,24,4,16,1,32,125,5,196,150,49,13,1,32,53,4,196, +18,196,132,134,65,1,32,120,5,196,150,67,20,1,32,45,4,196,36,13,4,196,129,90,32,83,5,196,150,85,8,5, +21,1,32,36,4,196,21,5,4,15,1,32,109,5,196,150,104,15,5,24,1,26,4,196,134,101,4,4,16,1,32,103, +5,196,150,123,22,5,29,1,14,4,196,134,81,2,4,19,1,32,95,5,196,150,142,31,5,26,1,32,126,4,26,1, +32,82,5,196,150,157,32,44,5,17,1,32,129,4,32,35,1,32,66,5,196,150,174,32,52,5,14,1,32,133,4,32, +38,1,32,54,5,196,150,191,32,57,5,16,1,32,140,4,32,41,1,32,37,5,196,150,208,32,61,5,25,1,32,143, +4,32,44,1,18,5,196,150,224,32,68,5,196,17,6,4,32,41,1,8,5,196,150,239,32,78,5,22,1,32,160,4, +32,33,1,27,16,32,88,5,196,14,6,4,17,1,27,16,32,95,5,196,123,32,39,4,196,130,164,32,103,5,26,1, +32,164,4,27,16,32,113,5,21,1,32,159,4,27,16,32,121,5,21,1,32,151,4,27,16,32,130,5,24,1,32,139, +4,27,16,32,136,5,32,35,1,32,122,4,27,16,32,147,5,32,34,1,32,112,4,27,16,32,156,5,32,38,1,32, +99,4,27,16,32,174,5,32,42,1,32,77,4,27,16,32,185,5,32,46,1,32,62,4,27,16,32,207,5,32,32,1, +32,54,4,27,16,32,225,5,21,1,32,47,4,27,16,32,233,5,32,39,1,21,4,27,16,32,240,5,32,46,1,7, +4,27,16,33,3,5,32,34,1,27,16,33,13,5,32,35,1,16,16,18,1,32,251,5,32,38,1,13,16,32,40,1, +32,245,5,23,1,12,16,65,4,32,61,1,32,232,5,14,1,12,16,23,4,32,61,1,32,220,5,4,1,12,16,196, +133,124,32,47,1,32,199,5,196,14,32,66,4,32,61,1,32,178,5,196,156,202,32,87,4,32,62,1,32,156,5,196, +156,214,32,102,4,4,1,196,100,27,1,32,135,5,4,1,11,16,32,101,4,5,1,9,5,4,1,12,6,32,61,1, +32,114,5,3,1,11,16,32,101,4,4,1,198,22,22,6,32,62,1,32,93,5,3,1,10,16,32,100,4,4,1,10, +5,196,41,32,44,6,32,62,1,32,71,5,196,23,32,99,4,200,21,18,6,3,1,2,6,32,62,1,32,49,5,196, +42,196,134,34,196,57,32,41,6,3,1,24,6,32,61,1,28,5,4,1,9,16,32,98,4,196,41,65,6,196,21,21, +6,32,65,1,4,5,3,1,9,16,32,97,4,196,21,198,21,22,6,32,50,1,9,16,32,96,4,198,15,22,6,29, +1,9,16,32,95,4,196,106,65,5,196,46,32,32,6,196,6,21,6,20,1,9,16,196,21,196,121,21,6,196,85,2, +6,9,1,32,67,6,15,1,10,16,32,94,4,196,85,3,6,3,1,18,6,17,1,32,70,6,196,139,254,32,93,4, +198,20,10,6,26,1,196,16,32,92,4,196,70,196,53,65,6,196,128,157,23,1,7,8,196,124,26,6,196,140,32,32, +92,4,196,54,65,6,22,1,13,8,198,19,32,91,4,200,128,211,17,6,19,1,23,8,198,33,32,90,4,196,120,196, +128,223,12,6,17,1,30,8,198,49,32,89,4,196,17,9,6,18,1,32,33,8,196,63,65,6,2,1,19,16,32,89, +4,200,129,5,7,6,12,1,6,8,3,1,32,33,8,2,1,32,73,6,196,27,196,134,37,198,129,27,4,6,11,1, +11,8,202,25,32,87,4,196,75,3,6,8,1,15,8,3,1,32,32,8,196,63,198,63,32,86,4,196,95,2,6,7, +1,18,8,200,21,32,85,4,5,1,198,129,76,196,35,4,8,198,72,65,1,196,69,32,85,4,196,128,245,196,129,133, +32,43,6,196,37,3,8,198,23,32,84,4,196,19,32,42,6,6,1,24,8,198,37,32,83,4,196,33,196,129,87,4, +1,25,8,196,51,32,73,6,196,163,203,65,16,32,82,4,5,1,196,129,148,196,129,112,3,1,27,8,200,26,32,82, +4,196,72,32,40,6,5,1,28,8,196,128,161,65,8,196,128,186,65,6,196,45,196,133,167,65,1,11,5,196,129,229, +198,30,65,8,202,27,32,80,4,200,41,2,8,202,36,32,79,4,5,1,10,5,5,1,32,51,6,196,93,7,8,196, +54,196,128,211,196,51,32,79,4,196,128,144,32,39,6,196,74,4,8,198,21,32,78,4,200,16,200,81,196,75,196,133, +60,198,73,198,24,65,8,196,14,32,76,4,196,59,65,5,196,130,56,32,38,6,196,40,2,8,196,33,32,76,4,196, +128,198,32,37,6,196,16,196,16,32,75,4,198,14,2,8,196,86,2,8,196,128,134,196,128,134,32,74,4,196,18,65, +8,196,128,152,198,128,152,196,128,150,196,133,9,2,1,196,128,149,32,36,6,196,64,4,8,198,25,196,133,26,198,128, +166,32,35,6,196,128,244,198,129,59,3,8,196,57,32,72,4,196,129,24,32,35,6,196,35,3,8,198,59,196,133,21, +196,128,198,32,34,6,196,129,22,17,8,196,128,226,2,8,196,129,130,2,6,196,129,14,32,70,4,196,130,54,196,128, +145,32,33,6,196,31,196,104,200,27,196,19,196,130,212,32,33,6,196,60,3,8,196,118,196,38,32,69,4,196,129,100, +32,32,6,196,62,4,8,196,18,32,68,4,196,50,31,6,196,31,198,31,32,67,4,196,61,30,6,196,25,2,8,196, +85,196,129,51,196,82,32,67,4,196,129,144,30,6,196,61,5,8,196,20,3,6,2,1,18,16,32,66,4,4,1,12, +5,196,130,133,16,6,196,25,196,128,202,196,128,218,198,27,32,65,4,196,121,29,6,196,42,198,61,198,40,32,64,4, +198,14,3,8,196,128,154,3,1,4,6,25,1,32,47,6,196,59,32,63,4,196,129,42,65,5,196,58,196,129,13,18, +8,196,128,183,65,8,32,34,1,32,45,6,196,87,32,63,4,196,63,196,24,196,129,23,3,8,198,21,32,62,4,196, +15,196,128,133,32,32,8,196,131,43,196,117,32,61,4,196,31,196,80,198,16,32,60,4,196,124,196,129,109,28,6,196, +128,146,198,130,123,32,35,8,196,131,76,196,165,206,196,26,196,131,184,200,23,65,8,196,16,32,59,4,196,76,4,8, +196,96,196,63,196,165,231,32,58,4,196,128,174,5,1,32,40,6,196,119,5,8,198,23,32,57,4,198,68,196,12,196, +107,196,32,32,57,4,196,129,44,196,77,9,8,198,46,32,56,4,196,129,56,196,89,9,8,196,128,153,196,130,209,65, +1,32,45,6,196,166,39,32,55,4,196,115,26,6,196,129,20,12,8,202,27,32,54,4,196,15,196,129,13,196,13,32, +53,4,196,128,206,196,132,45,26,6,196,129,29,13,8,202,55,32,53,4,196,129,122,27,6,196,104,5,8,196,128,221, +32,34,8,196,128,156,32,52,4,196,64,196,19,196,128,217,196,17,32,51,4,196,13,196,128,215,3,8,196,128,181,196, +134,249,13,5,196,64,196,46,196,129,39,198,128,199,196,135,8,12,5,5,1,196,130,52,31,8,198,62,196,137,74,13, +5,196,132,137,25,6,196,93,196,129,71,32,36,8,196,128,231,32,48,4,196,128,253,25,6,196,112,196,128,222,198,128, +250,32,47,4,196,17,196,125,198,110,196,128,246,32,47,4,196,129,25,24,6,196,128,140,196,129,23,4,8,196,129,22, +32,46,4,196,20,196,128,156,196,130,204,32,38,8,196,129,39,32,45,4,196,65,196,128,158,196,18,196,129,39,32,44, +4,196,14,8,8,196,128,169,2,8,196,129,67,32,43,4,196,129,156,196,130,198,25,6,197,1,128,189,2,8,2,1, +32,47,6,2,1,17,16,32,43,4,196,42,196,129,152,32,36,8,200,21,32,42,4,196,55,196,128,195,202,33,32,41, +4,196,130,5,2,5,196,128,166,196,128,241,196,129,54,32,37,8,200,53,32,40,4,200,23,10,8,196,87,65,8,200, +66,32,40,4,196,129,59,197,1,100,196,14,4,1,32,35,4,196,129,9,11,8,198,28,8,1,30,4,198,60,65,6, +197,1,39,198,102,196,139,166,15,1,22,4,196,17,196,129,54,11,8,196,57,65,8,196,21,65,0,25,1,11,4,196, +47,196,118,65,8,196,133,41,2,6,196,139,135,8,0,23,1,5,4,196,56,196,130,58,2,8,6,1,14,8,196,100, +198,28,15,0,24,1,14,5,196,129,132,196,23,65,8,13,1,196,128,234,2,8,198,50,25,0,13,1,196,23,196,131, +111,7,8,13,1,2,8,16,1,196,129,66,3,8,198,74,31,0,7,1,14,5,196,133,58,12,6,196,131,140,21,1, +8,8,9,1,196,130,39,32,38,8,198,102,32,33,0,198,128,207,5,1,25,8,29,1,7,8,10,1,198,129,194,4, +8,198,125,32,32,0,196,24,18,8,25,1,9,10,3,1,2,8,198,100,200,128,146,31,0,196,128,190,4,1,13,8, +22,1,18,10,3,1,65,8,11,1,198,128,242,200,128,171,30,0,196,130,222,196,129,18,196,29,196,128,142,27,10,198, +28,6,1,196,123,200,128,195,30,0,198,129,42,196,50,196,139,226,20,10,3,1,3,8,66,1,8,14,1,196,27,29, +0,198,73,3,1,196,20,7,1,8,10,3,1,196,118,65,1,196,128,142,200,128,242,28,0,196,129,89,196,129,138,65, +6,3,1,14,8,3,1,19,10,11,1,7,10,4,1,4,8,11,1,196,128,227,196,128,227,27,0,196,106,65,6,196, +128,132,198,64,65,1,7,10,3,1,2,8,196,128,133,196,128,252,27,0,198,128,156,65,8,3,1,5,10,9,1,6, +10,196,110,196,13,198,34,9,1,13,8,196,129,89,196,129,27,26,0,196,129,120,196,58,206,34,196,80,19,1,196,129, +22,196,129,46,25,0,200,110,65,1,15,8,198,52,5,10,3,1,5,8,3,1,198,81,18,1,196,29,24,0,196,100, +198,128,130,198,77,3,1,4,8,2,1,198,77,8,10,3,1,3,8,17,1,196,56,24,0,196,71,196,130,32,65,1, +196,51,196,28,196,55,65,1,196,34,198,128,135,65,1,3,8,197,1,128,223,196,27,196,90,23,0,196,129,224,2,6, +196,128,192,196,128,141,196,41,3,8,196,128,146,200,94,196,130,84,196,76,196,40,197,1,61,32,78,8,198,129,216,22, +0,198,128,233,196,128,200,196,77,196,128,171,196,128,180,198,128,175,197,1,109,196,80,2,1,196,131,35,196,134,167,196, +129,248,21,0,196,128,234,196,128,235,196,67,4,1,196,71,196,128,208,196,129,9,3,8,196,128,216,196,74,196,130,43, +196,129,213,21,0,196,128,211,3,8,4,1,196,128,141,198,128,146,200,129,15,196,128,155,11,8,197,1,129,104,32,75, +8,196,130,61,65,6,196,169,161,5,16,20,0,196,130,173,5,1,32,39,6,196,129,186,3,8,3,1,198,129,30,4, +10,196,128,149,196,128,253,196,111,3,6,196,38,19,0,196,128,143,4,8,3,1,6,10,3,1,4,8,2,1,2,10, +6,1,4,8,4,1,6,10,196,52,196,130,90,5,8,200,79,18,0,196,129,121,196,128,217,196,40,200,129,46,7,1, +196,128,249,24,8,196,130,179,4,8,196,131,45,2,6,196,106,18,0,196,128,249,196,98,198,129,30,196,129,74,5,1, +6,8,196,129,79,65,8,196,129,199,196,130,103,200,38,17,0,198,129,217,196,63,198,97,3,8,2,1,10,8,198,129, +107,65,1,196,130,155,200,63,16,0,198,129,242,2,6,196,128,155,196,129,12,16,8,198,128,132,196,129,106,2,1,198, +40,32,70,8,198,91,15,0,196,31,196,129,238,196,129,156,196,129,109,18,8,196,128,146,198,129,138,196,59,5,8,196, +118,196,170,152,2,16,15,0,196,129,110,196,34,196,129,104,16,8,196,113,196,129,244,65,1,198,131,129,6,8,198,32, +14,0,198,59,196,111,12,8,198,23,65,1,196,131,161,196,20,13,0,196,106,196,105,65,8,196,128,214,196,130,92,200, +129,184,3,1,196,131,77,196,129,248,196,132,148,9,8,198,81,12,0,196,128,138,3,6,196,130,120,196,129,212,18,8, +196,128,191,65,8,17,1,196,131,63,196,79,200,28,202,129,29,15,8,13,1,5,8,7,1,3,8,196,130,219,196,99, +11,0,196,48,198,130,166,3,8,196,42,198,27,6,1,7,8,196,129,120,2,8,196,129,118,65,6,196,128,155,10,0, +196,78,196,130,166,196,75,65,8,2,1,8,8,196,130,205,196,128,152,198,30,9,0,196,132,55,2,5,196,137,0,196, +130,223,196,85,11,8,2,1,25,8,196,131,248,5,8,198,58,196,29,196,134,182,196,24,10,8,196,130,250,196,128,197, +198,131,254,2,6,196,128,229,8,0,196,128,152,196,128,185,196,126,24,8,3,1,5,8,9,1,196,128,226,198,30,7, +0,196,128,175,196,128,175,4,8,198,26,196,131,231,4,1,196,128,199,198,52,6,0,196,97,196,134,238,196,129,232,2, +8,196,136,132,20,8,3,1,200,30,5,0,196,134,120,196,141,29,32,46,6,196,131,85,6,8,196,135,165,196,132,226, +196,129,137,196,129,226,2,1,196,131,50,196,128,187,198,111,5,0,196,129,0,4,6,196,35,65,8,196,135,198,65,8, +2,1,7,8,7,1,196,129,78,200,128,214,4,0,196,88,196,130,211,8,8,196,136,133,8,8,196,129,103,7,1,196, +132,244,8,8,198,128,239,3,0,198,28,198,132,148,17,8,196,129,79,9,1,196,130,44,3,8,198,129,5,2,0,198, +116,196,50,196,130,66,196,131,95,15,8,196,129,106,196,108,2,1,196,132,111,196,137,128,4,6,196,129,186,2,0,196, +110,196,131,197,14,8,196,136,161,4,1,2,8,3,1,9,8,6,1,200,34,65,0,196,128,193,196,128,215,11,8,196, +132,142,196,130,206,6,8,4,1,196,132,193,196,107,196,128,216,198,128,157,9,8,196,132,165,196,130,85,196,129,228,196, +104,196,128,176,196,43,196,60,196,131,193,11,8,196,128,183,196,40,196,149,141,196,138,170,15,6,196,137,74,65,1,196, +59,196,137,206,196,133,82,196,128,166,196,149,105,196,25,196,129,172,17,8,32,32,1,23,8,196,129,116,32,59,8,196, +129,170,196,131,31,4,16,65,1,16,5,196,136,188,196,32,196,129,238,13,1,23,8,196,133,133,9,8,198,30,198,22, +4,8,196,129,157,24,8,196,133,149,196,17,196,15,32,51,8,196,133,159,196,27,15,5,196,136,233,196,129,12,32,67, +8,196,133,175,196,43,15,5,196,129,243,196,128,155,32,58,8,196,133,191,10,8,198,88,15,5,196,131,17,5,6,196, +133,205,13,8,196,133,210,196,20,198,133,118,16,6,196,133,220,13,8,196,133,225,10,8,196,130,35,196,132,221,14,16, +198,133,141,15,6,196,133,243,196,133,205,11,8,196,131,68,2,6,196,173,105,65,16,196,24,196,134,7,196,133,225,202, +21,196,34,196,134,17,13,8,196,134,22,202,33,196,46,196,134,29,13,8,196,134,34,202,45,13,5,198,116,196,134,29, +12,8,200,54,202,12,202,14,196,16,196,134,56,196,14,196,23,196,134,63,11,8,196,130,129,198,74,198,136,157,11,6, +196,134,78,196,134,40,12,8,196,19,196,16,196,134,90,196,134,52,196,13,196,26,196,134,100,196,134,62,196,23,196,36, +196,134,110,13,8,196,134,115,196,35,196,48,196,134,122,13,8,196,134,127,196,47,196,60,196,134,134,13,8,196,134,139, +196,59,11,5,196,130,207,5,6,196,129,49,196,134,17,196,74,196,16,196,128,233,196,122,196,130,153,198,128,172,196,14, +196,134,174,13,8,196,112,196,23,196,134,183,196,10,196,30,196,134,190,196,17,11,5,196,128,254,196,134,200,13,8,196, +134,205,196,32,10,5,198,129,31,198,131,3,32,81,8,196,128,154,196,16,196,134,226,14,8,196,131,36,196,137,73,14, +16,196,31,196,134,241,200,16,196,38,196,134,248,200,23,196,45,196,134,255,14,8,7,1,32,48,6,2,1,14,16,9, +5,196,126,196,135,17,12,8,196,135,22,15,8,10,1,32,45,6,196,24,196,22,196,135,36,12,8,196,135,41,196,131, +142,32,42,6,196,42,9,5,198,129,129,196,135,57,15,8,4,1,3,10,10,1,32,38,6,196,63,196,22,196,135,75, +198,19,2,10,11,1,32,35,6,196,174,192,196,39,196,135,92,196,18,4,10,10,1,32,32,6,196,174,209,9,5,196, +129,166,196,135,112,12,8,196,135,117,196,135,11,2,1,14,10,7,1,31,6,196,174,236,8,5,196,128,247,196,135,139, +12,8,196,137,162,26,8,31,1,16,10,5,1,65,10,30,6,196,175,8,196,29,196,135,164,196,26,6,8,32,50,1, +23,10,196,140,134,65,6,196,175,30,196,51,196,135,186,196,48,196,132,184,20,1,10,6,4,1,10,10,7,1,6,10, +2,1,28,6,196,175,59,196,80,196,135,215,196,30,65,1,29,6,3,1,8,10,11,1,4,10,196,57,196,56,196,103, +196,135,238,196,130,185,20,8,196,141,12,196,133,104,13,1,198,25,8,5,196,130,59,196,130,169,32,52,8,196,138,29, +4,8,196,141,38,198,133,146,196,134,208,3,1,3,10,196,141,151,16,6,196,175,139,7,5,196,129,150,202,38,196,23, +2,1,8,10,2,1,3,10,3,1,65,6,20,1,8,6,196,175,167,196,29,198,63,3,8,196,56,196,135,24,65,10, +196,54,31,1,196,128,160,200,21,9,10,3,1,196,147,1,7,1,2,18,196,175,203,196,65,196,131,11,32,51,8,196, +99,196,128,192,196,140,193,2,1,196,134,252,10,10,3,1,2,10,4,1,4,18,21,1,4,18,196,175,241,7,5,196, +130,198,196,131,243,32,63,8,202,42,9,10,4,1,196,148,142,30,18,196,176,10,6,5,196,130,21,196,26,196,63,5, +6,16,1,196,135,54,9,10,196,128,158,3,1,196,29,196,26,200,128,187,196,129,19,5,6,3,1,10,18,3,1,16, +10,4,1,2,10,4,1,196,55,196,52,196,128,213,196,132,233,17,8,196,129,50,4,6,200,32,65,10,196,53,31,18, +196,176,87,196,78,198,128,141,65,8,200,24,2,1,4,10,196,56,21,18,196,176,107,196,98,196,137,12,196,129,125,5, +8,196,129,66,4,6,3,1,9,18,198,147,134,196,48,5,5,196,130,142,200,100,9,18,198,135,224,65,1,9,10,198, +48,198,19,8,18,4,1,5,10,13,1,6,10,2,1,32,32,18,196,176,169,196,39,196,83,198,128,134,8,18,196,129, +109,196,147,245,25,18,196,176,189,202,21,196,129,120,16,1,16,18,196,176,201,198,33,7,18,196,128,157,65,10,30,1, +14,18,2,1,13,16,198,50,198,18,196,148,100,3,10,7,1,198,18,4,5,196,128,222,196,129,124,65,8,198,128,202, +6,18,196,148,207,4,10,7,1,24,10,198,44,196,142,170,32,56,6,196,137,161,196,139,32,6,8,196,128,155,196,136, +160,6,18,196,75,3,1,196,129,106,21,10,198,77,196,34,196,137,190,196,139,61,196,136,69,32,33,6,3,1,5,18, +196,63,65,10,200,32,196,58,196,137,214,196,25,32,32,6,196,21,65,18,196,124,197,1,149,168,196,136,55,15,10,2, +1,12,18,196,128,130,196,90,196,137,246,206,33,8,1,7,10,3,1,11,18,2,1,13,16,4,5,196,132,66,3,6, +196,138,19,196,134,12,28,8,196,128,140,3,1,8,10,9,1,5,10,196,128,252,2,1,200,42,65,1,12,16,3,5, +196,131,157,196,133,19,198,130,45,196,133,63,196,96,200,130,93,196,136,249,2,1,196,150,7,196,129,202,7,10,196,96, +196,172,183,204,38,3,1,7,10,13,1,3,10,3,1,5,10,6,1,2,10,3,1,8,10,196,28,198,25,196,25,197, +1,130,86,196,131,34,198,120,65,18,196,172,225,200,80,196,138,244,32,34,6,2,1,5,18,196,53,198,136,8,65,1, +198,54,196,149,151,196,130,75,196,128,152,196,36,196,112,196,129,217,32,81,8,196,129,30,196,129,229,2,6,2,1,4, +18,196,130,208,196,131,117,196,87,196,130,108,196,137,162,9,10,196,42,196,134,218,15,6,196,138,198,196,140,1,6,8, +202,41,12,1,4,10,196,137,194,196,129,220,2,18,196,173,69,196,31,196,138,225,198,28,65,10,9,1,6,10,3,1, +196,130,120,5,10,196,94,196,53,196,138,247,196,50,196,130,59,196,137,243,3,18,196,150,124,196,148,147,196,130,191,196, +128,199,198,128,157,196,83,196,139,21,202,31,14,10,196,131,7,196,129,247,196,50,196,100,196,139,43,6,8,196,130,80, +2,8,200,53,14,10,198,136,179,196,150,62,196,74,2,5,196,129,54,5,6,196,139,67,196,141,119,8,8,196,28,3, +1,4,10,14,1,196,99,196,144,64,17,6,196,139,90,196,24,196,130,81,2,6,3,1,2,18,3,1,8,10,6,1, +12,10,3,1,4,10,12,1,198,129,3,196,37,196,139,123,196,141,175,7,8,198,128,222,198,151,114,196,148,248,196,130, +147,196,67,196,149,29,6,10,196,129,1,196,69,196,139,155,204,33,196,132,38,196,131,220,5,1,3,18,196,174,19,196, +89,196,139,175,198,136,205,32,34,8,198,129,19,196,151,167,198,131,141,65,1,4,10,198,131,148,24,10,13,1,196,128, +243,196,123,196,139,209,196,142,74,8,8,198,129,52,196,149,104,196,130,230,5,1,196,130,253,24,10,12,1,196,68,196, +133,92,9,6,196,139,242,202,34,196,150,254,198,129,162,19,10,196,129,106,196,22,196,140,4,200,52,198,128,179,3,10, +196,150,94,196,129,124,196,40,196,140,22,198,70,196,150,107,2,10,196,19,196,54,196,140,36,196,142,37,8,8,196,128, +206,65,6,196,150,68,196,23,196,74,196,134,220,32,41,8,196,130,185,3,8,196,24,6,1,10,10,2,1,13,10,5, +1,7,10,9,1,8,10,196,129,192,196,129,27,196,135,84,32,46,8,196,130,220,2,8,198,129,193,198,132,164,2,1, +196,139,86,196,138,198,196,130,122,65,10,196,130,141,196,130,7,208,40,198,152,117,196,138,217,196,55,196,130,157,2,18, +196,179,219,65,16,196,129,86,196,145,90,196,135,10,24,8,202,62,198,152,148,134,94,3,10,197,1,138,31,196,132,96, +5,10,200,40,198,32,23,8,196,121,198,130,19,196,152,83,4,1,196,153,118,196,132,144,196,153,124,196,39,196,130,138, +198,130,118,3,10,200,75,196,129,152,4,6,196,142,20,196,130,205,3,8,196,129,231,3,1,10,10,3,1,65,10,5, +1,2,10,4,1,200,46,2,10,3,1,14,18,196,110,196,41,196,142,72,196,131,95,2,8,202,38,197,2,151,178,4, +10,196,130,206,198,130,178,198,31,196,142,139,24,6,196,142,86,196,137,71,31,8,198,132,99,196,133,98,196,131,120,198, +132,178,196,139,133,196,152,246,196,37,196,34,196,135,222,32,36,8,196,128,224,196,98,196,132,29,65,10,196,128,223,2, +10,196,150,190,198,138,209,8,10,198,97,32,71,6,196,142,150,198,145,170,12,8,198,128,131,10,10,3,1,3,10,198, +31,196,121,3,1,11,16,32,72,6,196,133,233,196,130,179,2,8,196,29,196,129,164,196,140,10,6,10,196,133,93,65, +10,198,33,32,72,6,196,142,206,196,29,196,128,184,196,132,233,10,10,196,151,16,196,131,215,196,129,31,65,18,196,147, +28,32,73,6,196,134,35,196,143,106,8,8,196,130,191,198,133,144,196,151,76,3,10,196,151,52,12,1,9,10,198,38, +32,73,6,196,143,11,196,136,71,21,8,196,130,222,2,1,6,10,16,1,4,10,200,32,32,74,6,196,134,90,196,130, +25,2,8,200,25,65,1,196,51,8,10,196,152,134,196,129,6,196,86,32,74,6,196,143,62,196,28,196,131,14,65,1, +6,10,196,152,157,195,1,131,96,9,10,196,154,5,65,10,196,35,32,75,6,196,143,75,9,8,198,130,202,196,32,196, +153,165,196,131,237,196,128,239,198,129,242,65,1,15,18,196,147,173,196,33,196,130,108,196,106,196,134,130,198,133,124,196, +140,150,196,134,139,196,56,32,76,6,196,143,146,196,131,49,196,129,251,196,129,24,6,10,5,1,196,153,86,196,132,15, +196,130,38,196,118,32,77,6,196,144,23,196,131,52,196,130,25,196,134,55,6,1,196,140,211,65,1,196,139,254,198,132, +48,13,10,196,128,235,196,128,235,32,77,6,196,144,255,196,129,193,65,8,198,131,253,65,1,9,10,14,1,2,10,7, +1,15,10,3,1,196,132,64,4,18,3,1,11,16,32,78,6,196,145,36,196,129,193,200,38,196,135,120,196,133,80,198, +133,153,196,154,147,16,18,196,148,69,32,79,6,196,145,64,196,129,192,200,66,4,10,7,1,5,10,6,1,6,10,13, +1,200,61,32,80,6,196,142,188,196,130,209,2,8,198,134,59,196,131,96,6,10,196,131,116,198,56,32,81,6,196,145, +113,196,129,149,2,8,196,133,108,196,133,160,196,133,160,26,10,196,129,121,2,18,196,148,148,32,82,6,196,145,143,196, +129,121,65,8,196,132,141,65,6,196,152,222,3,10,198,88,10,1,196,132,106,5,18,3,1,11,16,32,83,6,196,145, +177,196,132,10,2,8,196,35,15,1,11,10,8,1,196,134,40,198,132,19,3,1,3,18,196,148,214,32,84,6,196,145, +209,196,129,132,196,31,19,1,5,10,8,1,5,10,196,30,65,1,2,6,3,1,11,16,32,85,6,196,146,1,196,128, +150,196,60,15,1,65,6,196,136,27,196,142,29,4,10,3,1,6,6,8,1,3,6,196,149,21,32,86,6,198,146,35, +196,137,109,5,6,11,1,204,35,11,6,3,1,11,16,32,88,6,196,146,80,196,131,134,198,140,5,196,19,196,132,106, +18,10,196,132,132,196,148,149,196,149,70,32,89,6,196,147,204,22,8,208,31,32,91,6,196,146,149,13,8,196,133,227, +4,6,2,1,196,148,182,198,40,196,141,166,32,93,6,198,146,236,12,8,196,132,22,196,148,201,9,10,196,155,73,196, +148,207,196,141,191,32,94,6,196,148,6,16,8,196,89,17,6,198,133,197,196,141,153,196,133,211,2,1,20,6,196,183, +121,32,96,6,196,148,56,196,147,33,196,57,2,6,196,134,28,196,130,100,196,149,4,2,6,196,183,148,32,97,6,196, +142,249,19,8,196,128,144,16,6,198,133,11,196,130,79,198,28,32,100,6,196,143,14,16,8,196,133,61,196,136,93,20, +6,196,133,35,196,136,173,196,136,1,196,79,32,103,6,196,143,41,196,136,218,196,134,192,32,32,6,197,2,133,62,196, +25,32,105,6,196,148,152,196,129,135,196,140,139,16,6,196,136,41,65,1,2,10,5,1,3,10,196,136,52,20,6,196, +150,10,32,108,6,196,148,185,196,130,203,198,145,48,18,6,196,134,158,65,10,196,128,244,3,6,196,150,37,32,111,6, +196,148,226,196,129,219,196,132,232,2,6,196,134,81,11,1,198,129,68,15,6,196,150,64,32,113,6,196,143,149,2,8, +196,133,196,196,142,188,32,65,6,196,156,111,196,136,199,196,136,232,21,6,196,150,95,32,114,6,196,149,61,196,132,206, +2,8,196,150,98,9,6,196,136,75,198,142,151,196,28,32,117,6,12,1,196,148,152,13,8,198,24,9,10,196,128,234, +65,6,196,150,141,32,121,6,13,1,196,131,77,65,8,196,46,196,131,182,196,132,242,198,27,32,124,6,16,1,32,45, +8,196,64,196,129,45,10,1,6,10,196,46,196,45,32,128,6,23,1,32,34,8,198,22,65,1,5,10,196,19,32,134, +6,32,55,1,32,65,6,196,129,81,198,128,220,3,10,196,38,32,135,6,196,150,36,26,1,32,65,6,196,134,220,5, +1,196,40,32,135,6,3,1,15,6,11,1,196,136,238,196,132,201,65,6,200,137,195,65,10,196,125,2,6,3,1,10, +16,198,31,32,101,6,196,137,213,200,134,210,23,6,196,151,25,32,134,6,196,150,166,19,6,3,1,12,10,4,1,8, +10,200,41,198,19,196,131,248,196,137,12,200,51,196,29,18,6,196,132,142,196,22,196,10,196,134,203,7,10,200,69,196, +19,198,135,4,2,10,196,129,205,4,6,196,151,86,32,133,6,198,62,196,137,139,65,10,196,137,13,198,22,196,16,196, +136,217,196,134,196,198,32,200,11,200,28,6,10,198,40,32,133,6,196,148,227,32,43,6,198,135,59,198,128,133,196,128, +255,32,132,6,196,113,2,6,197,1,132,92,10,10,196,151,102,65,6,196,180,224,208,22,208,24,198,26,196,138,208,200, +20,32,132,6,196,51,2,6,196,13,32,131,6,196,42,196,130,203,9,10,196,151,220,13,6,196,181,6,200,19,196,151, +66,196,181,14,196,9,196,151,129,196,32,65,6,25,10,196,151,83,196,151,141,32,130,6,196,85,2,6,28,1,26,6, +3,1,9,16,206,16,198,18,16,1,9,6,196,30,198,26,32,54,6,196,151,174,198,9,32,129,6,196,36,32,55,6, +196,151,187,200,12,200,14,200,16,32,129,6,196,105,32,57,6,196,151,204,32,128,6,196,29,65,6,196,151,214,32,128, +6,196,39,196,11,32,128,6,196,46,196,18,32,128,6,196,53,196,25,32,128,6,196,43,196,32,32,127,6,196,67,2, +6,196,151,252,32,127,6,196,77,196,11,32,127,6,196,84,196,18,32,127,6,196,91,196,25,32,126,6,196,98,3,6, +196,152,27,32,126,6,196,108,196,11,32,126,6,196,115,4,6,2,1,9,16,32,126,6,196,126,198,12,32,126,6,196, +128,133,198,20,32,125,6,196,128,141,5,6,196,28,32,125,6,196,128,151,196,11,32,125,6,196,128,159,196,19,32,125, +6,196,128,167,196,27,32,125,6,196,128,175,196,35,32,124,6,196,128,183,6,6,196,160,93,32,124,6,196,128,194,196, +12,32,124,6,196,128,202,196,20,32,124,6,196,128,210,196,28,32,124,6,196,128,218,196,36,32,123,6,196,128,226,7, +6,196,160,136,32,123,6,196,128,237,196,12,32,123,6,196,128,245,196,20,32,123,6,196,128,253,196,28,14,1,32,109, +6,196,128,246,196,38,30,1,32,92,6,196,129,17,8,6,196,160,183,32,46,1,32,76,6,196,129,31,196,15,16,4, +32,46,1,32,60,6,196,129,44,196,28,32,33,4,32,45,1,32,44,6,196,129,58,196,42,196,162,46,15,1,27,6, +196,129,70,9,6,196,160,236,32,65,4,32,46,1,10,6,196,129,86,196,17,196,151,160,32,42,1,32,182,6,196,161, +5,32,97,4,196,82,32,90,6,196,161,16,32,113,4,196,93,32,74,6,196,161,27,196,155,13,196,140,215,32,99,6, +196,161,39,32,146,4,196,89,196,24,32,161,4,196,123,26,6,196,161,56,32,178,4,196,106,32,43,6,196,183,38,32, +194,4,196,117,27,6,196,183,48,32,210,4,196,98,32,44,6,196,183,59,32,226,4,196,109,28,6,196,183,69,32,242, +4,196,119,12,6,196,183,79,33,3,4,32,45,1,6,6,196,183,90,33,19,4,32,37,1,8,16,33,35,4,21,1, +8,16,33,51,4,196,187,144,33,52,4,196,153,166,196,7,196,9,196,11,196,13,196,15,33,52,4,196,183,72,196,7, +194,31,196,154,13,14,4,5,1,33,32,4,3,1,10,16,10,4,4,5,11,1,33,26,4,3,1,10,16,7,4,7, +5,17,1,33,20,4,3,1,10,16,6,4,13,5,19,1,33,13,4,196,154,62,196,13,196,39,12,1,33,5,4,196, +154,74,196,25,9,5,23,1,33,0,4,196,154,86,5,4,30,5,16,1,33,0,4,2,1,11,16,196,14,7,5,7, +1,65,5,196,28,65,16,196,13,8,5,196,156,53,32,149,4,196,154,143,4,4,32,46,5,198,15,4,4,32,45,5, +4,1,32,253,4,3,1,11,16,3,4,32,46,5,196,36,65,4,3,1,11,16,196,14,196,156,98,32,52,4,10,1, +32,88,4,196,154,193,2,4,32,46,5,4,1,32,156,4,20,1,32,78,4,3,1,11,16,65,4,32,47,5,198,39, +19,1,32,70,4,196,136,230,32,48,5,196,52,4,1,196,157,192,14,1,32,62,4,196,136,248,32,47,5,196,19,6, +5,4,1,196,156,198,196,159,44,198,90,65,16 +}; + +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe github.png + +const unsigned short data_github_png::pal[2] PROGMEM_FAR = +{ +0,65535 +}; + +const unsigned char data_github_png::data[109] PROGMEM_FAR = +{ +132,6,10,0,0,0,0,0,4,6,6,2,64,0,6,129,0,0,0,0,0,0,0,0,0,0,0,96,45,1,2,235, +134,53,240,100,110,1,32,220,141,150,70,213,54,251,216,166,55,178,205,175,7,192,182,134,149,145,166,192,104,100,108,104, +146,54,13,0,248,69,13,22,198,70,216,100,198,38,53,216,177,40,3,237,158,236,126,215,68,136,244,202,40,241,165,172, +244,250,145,70,176,79,51,97,39,129,0,0,4 +}; + +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe -b 0 1 -s 5 -p exo mdscroll.png + +const unsigned short data_mdscroll_png::pal[10] PROGMEM_FAR = +{ +0,49264,58760,47913,58474,56832,37415,39970,52610,34891 +}; + +const unsigned char data_mdscroll_png::data[16520] PROGMEM_FAR = +{ +8,136,136,136,130,136,70,0,8,134,140,102,104,32,0,14,2,72,8,198,128,0,10,0,2,70,64,1,0,0,65,0, +125,0,180,0,235,0,27,1,79,1,132,1,183,1,232,1,27,2,79,2,130,2,178,2,227,2,19,3,72,3,124,3, +180,3,235,3,32,4,86,4,138,4,190,4,242,4,40,5,96,5,154,5,208,5,3,6,55,6,103,6,151,6,204,6, +5,7,66,7,125,7,177,7,236,7,37,8,93,8,150,8,208,8,11,9,72,9,132,9,189,9,247,9,49,10,109,10, +170,10,230,10,32,11,90,11,147,11,202,11,0,12,56,12,106,12,156,12,203,12,251,12,40,13,86,13,130,13,176,13, +222,13,14,14,64,14,117,14,169,14,223,14,22,15,79,15,137,15,191,15,247,15,44,16,102,16,159,16,214,16,12,17, +68,17,122,17,175,17,231,17,28,18,80,18,135,18,184,18,237,18,34,19,85,19,139,19,193,19,245,19,44,20,97,20, +149,20,200,20,253,20,45,21,94,21,146,21,201,21,252,21,47,22,96,22,146,22,197,22,250,22,47,23,101,23,153,23, +206,23,2,24,53,24,102,24,152,24,198,24,246,24,37,25,86,25,137,25,191,25,246,25,47,26,98,26,152,26,206,26, +3,27,55,27,109,27,167,27,226,27,20,28,73,28,125,28,172,28,218,28,10,29,57,29,107,29,157,29,206,29,2,30, +53,30,103,30,153,30,205,30,1,31,55,31,109,31,163,31,216,31,15,32,70,32,126,32,179,32,232,32,26,33,79,33, +132,33,186,33,237,33,29,34,75,34,125,34,170,34,217,34,7,35,56,35,104,35,152,35,196,35,235,35,17,36,55,36, +98,36,145,36,192,36,237,36,26,37,69,37,113,37,157,37,201,37,242,37,26,38,67,38,113,38,155,38,193,38,239,38, +28,39,76,39,124,39,175,39,225,39,19,40,66,40,117,40,166,40,217,40,11,41,58,41,106,41,155,41,202,41,249,41, +40,42,86,42,136,42,185,42,235,42,28,43,78,43,129,43,180,43,227,43,18,44,66,44,114,44,160,44,205,44,251,44, +43,45,92,45,137,45,184,45,230,45,21,46,69,46,117,46,165,46,216,46,9,47,56,47,104,47,155,47,205,47,255,47, +50,48,98,48,147,48,193,48,240,48,31,49,79,49,127,49,176,49,226,49,15,50,63,50,111,50,161,50,209,50,1,51, +42,51,86,51,129,51,172,51,216,51,255,51,41,52,83,52,125,52,171,52,218,52,10,53,56,53,104,53,151,53,192,53, +228,53,8,54,46,54,86,54,124,54,163,54,202,54,239,54,20,55,56,55,94,55,134,55,177,55,220,55,9,56,54,56, +98,56,144,56,193,56,240,56,33,57,81,57,127,57,172,57,219,57,12,58,62,58,110,58,160,58,209,58,2,59,52,59, +103,59,154,59,204,59,251,59,41,60,86,60,134,60,179,60,222,60,9,61,55,61,100,61,145,61,193,61,0,73,1,32, +228,2,3,4,133,164,5,193,178,128,72,95,4,3,68,178,2,136,252,184,19,46,162,6,81,7,36,8,129,180,122,85, +24,145,19,242,128,228,137,121,149,49,16,152,153,18,227,37,165,192,195,115,9,36,152,23,32,160,0,0,2,0,73,1, +32,228,2,3,4,133,164,5,129,178,160,233,9,19,40,130,129,176,38,93,6,73,8,80,163,7,194,168,206,17,226,22, +35,120,134,209,81,26,40,94,89,37,13,43,69,219,150,2,110,159,203,3,144,80,0,0,1,0,73,1,32,237,2,3, +4,162,73,5,131,101,65,210,20,38,56,4,203,121,6,7,8,80,114,236,146,41,98,35,228,101,149,86,137,165,161,52, +132,114,134,213,162,237,205,165,191,151,3,99,0,64,0,4,0,73,1,32,237,2,3,4,162,73,5,3,101,129,218,20, +38,56,4,203,185,6,7,8,80,114,236,146,49,178,39,8,237,12,41,69,83,9,165,149,44,58,172,23,110,109,131,249, +3,118,52,0,0,0,64,0,73,1,32,237,2,3,4,162,73,5,3,101,129,218,21,38,62,26,6,7,73,8,131,104, +237,5,200,138,42,230,147,10,164,157,86,11,183,54,193,252,3,187,26,0,0,0,32,0,73,1,32,237,2,3,4,162, +73,5,3,101,129,218,20,38,54,4,139,105,6,7,148,8,7,8,240,178,41,146,34,104,71,21,83,9,132,82,14,129, +118,230,216,63,151,3,99,0,64,0,4,0,73,1,32,237,2,3,4,162,73,5,3,101,129,218,20,38,54,4,139,116, +6,7,148,8,7,40,89,18,6,143,11,16,187,46,163,24,85,140,38,17,45,58,5,219,154,96,105,141,0,0,0,16, +0,73,1,32,237,2,3,4,162,73,5,3,101,129,218,20,38,54,4,139,121,6,7,8,80,114,236,178,35,151,104,84, +133,11,32,21,3,6,11,36,157,2,237,205,48,52,198,0,128,0,8,0,73,1,32,237,2,3,4,162,73,5,3,101, +129,218,20,38,54,4,139,121,6,7,8,80,114,236,154,33,162,39,8,89,0,176,23,48,93,36,248,29,91,231,5,24, +208,0,0,1,0,73,1,32,237,2,3,4,162,72,5,131,101,193,226,20,38,54,4,139,185,6,7,8,80,114,236,170, +43,132,118,116,134,5,92,11,25,151,7,73,62,6,86,242,20,125,141,0,0,0,16,0,73,1,32,237,2,3,4,162, +72,5,131,101,193,226,22,38,56,68,156,6,7,8,168,57,118,85,21,195,164,42,64,142,204,32,44,152,23,50,93,36, +248,25,91,200,81,246,52,0,0,0,64,0,73,1,32,237,2,3,4,162,72,5,131,101,193,226,22,38,56,68,156,6, +7,8,168,57,118,73,20,209,11,132,44,124,170,42,209,11,25,44,146,116,12,55,40,228,251,26,0,0,0,32,0,73, +1,32,228,2,3,4,133,168,5,141,151,7,136,88,152,225,18,114,6,7,8,160,229,217,36,83,68,46,16,138,49,212, +146,198,75,36,157,3,173,208,251,26,0,0,0,32,0,73,1,32,228,2,3,4,133,168,5,141,151,7,136,88,152,225, +18,114,6,7,8,160,229,217,36,83,36,45,149,32,36,117,130,201,69,140,150,73,58,7,155,159,99,0,64,0,4,0, +73,1,32,228,2,3,4,133,168,5,132,199,74,141,20,38,52,4,203,185,6,7,8,80,114,236,146,41,146,22,208,210, +138,80,11,153,44,146,124,15,183,56,198,0,128,0,8,0,73,1,32,228,2,3,4,133,168,5,141,150,7,104,96,152, +225,31,6,7,8,42,14,144,146,41,146,22,208,185,244,70,73,33,151,85,232,7,80,193,20,163,192,125,187,72,105,141, +0,0,0,16,0,73,1,32,228,2,3,4,197,168,5,178,192,237,12,19,28,35,229,6,7,8,65,210,18,69,50,66, +218,21,60,72,9,162,45,11,24,66,233,71,0,179,114,169,197,30,154,99,0,64,0,4,0,73,1,32,228,2,3,4, +197,168,5,141,149,7,72,88,152,185,1,82,62,6,7,8,80,28,58,66,72,134,72,91,66,202,106,33,33,66,170,193, +164,178,41,71,0,179,115,45,154,67,76,104,0,0,0,128,0,73,1,32,228,2,3,4,197,168,5,141,161,97,130,228, +197,200,86,76,183,6,7,148,8,7,14,138,161,184,44,66,218,19,62,76,9,10,147,26,42,100,194,89,192,52,220,163, +83,172,104,0,0,0,128,0,73,1,32,237,2,3,4,147,10,5,44,176,185,130,98,228,40,76,183,6,7,73,8,48, +109,209,36,51,100,8,100,249,97,52,38,101,37,8,210,206,1,150,229,80,154,67,108,104,0,0,0,128,0,73,1,32, +237,2,3,4,147,10,5,44,176,185,146,98,228,68,203,105,6,7,147,8,6,221,21,67,112,143,202,144,33,99,200,77, +16,168,209,179,46,159,28,2,205,204,53,105,13,177,160,0,0,2,0,73,1,32,237,2,3,4,147,10,5,33,81,99, +132,198,203,57,6,7,8,32,105,28,149,43,112,143,208,169,9,83,168,133,5,10,13,155,50,233,241,192,52,220,163,70, +78,49,160,0,0,2,0,73,1,32,237,2,3,4,148,10,5,20,20,67,132,198,203,57,6,7,8,32,105,11,21,81, +6,250,25,33,146,5,2,130,133,6,205,153,116,248,224,22,110,93,163,40,113,141,0,0,0,16,0,73,1,32,237,2, +3,4,148,10,5,33,65,66,228,198,128,161,47,6,7,8,36,13,33,98,170,6,63,67,4,50,5,64,108,84,217,151, +79,142,1,102,229,154,45,16,227,26,0,0,0,32,0,73,1,32,237,2,3,4,149,10,5,18,18,70,77,147,26,43, +68,228,6,7,8,129,164,44,149,6,71,232,84,133,217,51,132,212,179,2,166,140,145,48,112,13,55,36,128,194,29,99, +0,64,0,4,0,73,1,32,237,2,3,4,149,10,5,33,33,53,151,38,48,5,137,185,6,7,8,32,105,11,37,81, +6,249,80,187,7,66,106,74,50,178,96,80,108,153,146,230,31,28,3,77,202,35,29,99,0,64,0,4,0,73,1,32, +237,2,3,4,150,10,5,45,22,180,52,76,108,169,19,6,7,146,8,6,144,178,85,6,8,143,149,34,120,72,225,19, +203,38,5,6,201,153,149,7,48,233,31,155,3,77,201,162,158,99,0,64,0,4,0,73,1,32,237,2,3,4,150,10, +5,19,25,52,76,116,171,6,7,146,8,6,144,177,249,6,50,2,132,111,9,28,34,121,100,192,144,217,180,180,108,137, +131,96,105,185,52,83,204,104,0,0,0,128,0,73,1,32,237,2,3,4,151,10,5,18,26,50,76,120,167,6,7,73, +8,16,105,9,158,48,64,76,141,217,35,136,133,5,16,160,161,51,38,76,27,3,45,202,37,158,99,0,64,0,4,0, +73,1,32,237,2,3,4,151,10,5,18,26,46,76,108,9,148,211,6,7,8,34,12,163,146,71,203,16,20,2,100,82, +5,74,74,204,10,18,3,70,13,129,150,228,210,207,177,160,0,0,2,0,73,1,32,237,2,3,4,152,10,5,45,16, +201,49,242,158,6,7,8,72,26,112,77,44,177,1,130,41,5,16,38,38,76,100,64,193,176,46,220,170,121,246,52,0, +0,0,64,0,73,1,32,237,2,3,4,152,10,5,45,16,201,49,242,158,6,7,8,72,26,112,77,44,177,1,146,42, +5,33,33,36,40,142,124,193,176,50,220,154,81,246,52,0,0,0,64,0,73,1,32,237,2,3,4,148,10,5,21,19, +52,76,88,168,177,70,6,7,146,8,32,210,23,43,146,6,31,38,68,232,186,154,180,66,136,199,204,27,3,45,232,5, +77,56,235,26,0,0,0,32,0,73,1,32,237,2,3,4,147,10,5,33,49,66,131,146,3,132,197,202,81,10,20,83, +6,7,8,34,12,161,114,178,99,228,200,157,21,68,42,169,24,48,134,12,27,3,45,202,44,203,57,34,148,0,0,0, +64,0,73,1,32,237,2,3,4,147,9,5,16,38,42,76,151,3,2,89,50,99,37,69,9,249,6,7,8,16,101,28, +22,44,114,240,137,1,66,35,40,203,50,20,6,40,94,132,82,205,129,102,229,214,132,67,114,41,0,64,0,4,0,73, +1,32,237,2,3,4,147,9,5,16,38,42,76,151,3,2,89,66,98,229,108,20,39,228,6,7,8,129,167,5,139,28, +149,32,70,204,17,20,66,48,98,8,133,146,205,129,150,228,217,173,16,220,138,80,0,0,1,0,73,1,32,237,2,3, +4,147,9,5,16,40,42,72,155,3,2,89,4,56,4,201,147,6,7,73,8,16,105,193,114,199,68,135,204,145,31,10, +6,10,151,153,110,93,153,169,20,160,0,0,2,0,73,1,32,237,2,3,4,147,9,5,16,36,44,80,83,3,2,89, +4,48,6,9,233,6,7,145,8,6,91,46,88,233,225,18,3,132,68,208,12,23,164,8,177,122,27,39,50,220,163,50, +46,77,72,165,0,0,0,16,0,73,1,32,228,2,3,4,197,164,5,128,144,185,65,77,3,2,100,16,232,19,45,78, +6,7,8,68,25,109,11,22,186,40,64,136,218,59,54,128,96,190,8,96,188,208,25,110,93,154,24,145,68,160,0,0, +2,0,73,1,32,228,2,3,4,197,164,5,128,144,185,65,77,3,2,100,16,232,19,45,77,6,7,36,8,65,167,5, +203,93,18,31,50,69,5,40,165,24,46,52,94,104,12,55,48,205,12,72,162,80,0,0,1,0,73,1,32,228,2,3, +4,197,164,5,64,152,185,65,93,3,2,100,16,232,20,46,166,6,7,8,68,25,108,185,105,49,242,132,72,76,241,85, +40,72,188,217,49,102,229,26,21,120,74,68,179,42,0,0,0,32,0,73,1,32,228,2,3,4,197,164,5,65,178,96, +76,91,3,2,89,4,58,5,75,249,6,7,8,16,101,178,229,174,158,18,32,48,71,5,42,130,96,188,201,113,160,44, +220,163,66,175,9,72,150,101,0,64,0,4,0,73,1,32,228,2,3,4,197,164,5,65,178,96,76,91,3,2,89,4, +60,5,11,244,6,7,162,8,50,218,23,46,118,72,125,9,145,149,58,73,83,50,90,100,184,208,24,110,69,153,103,132, +164,75,50,160,0,0,2,0,73,1,32,228,2,3,4,133,164,5,129,178,96,76,91,3,2,89,4,60,5,12,83,6, +7,8,34,13,161,114,226,35,229,1,195,164,38,116,210,170,147,6,5,168,240,218,180,97,185,38,101,158,145,145,44,202, +0,128,0,8,0,73,1,32,228,2,3,4,133,164,5,129,178,96,76,91,3,2,89,4,60,5,76,105,6,7,8,68, +101,178,69,198,71,208,153,25,115,173,22,68,66,197,104,229,14,43,38,27,146,102,89,233,25,18,204,168,0,0,0,128, +0,73,1,32,228,2,3,4,133,164,5,129,178,96,76,91,3,2,89,4,60,5,140,244,6,7,8,70,26,46,92,232, +152,250,22,35,44,167,161,116,51,5,134,192,195,114,237,8,188,35,34,89,149,0,0,0,16,0,73,1,32,228,2,3, +4,133,164,5,65,178,128,76,95,3,2,89,4,62,5,77,105,6,7,8,69,180,96,185,219,215,229,1,195,164,46,113, +12,33,23,44,56,172,150,110,85,169,87,196,100,75,50,160,0,0,2,0,73,1,32,228,2,3,4,133,164,5,65,178, +128,76,99,3,2,89,4,62,5,77,211,6,7,8,69,180,96,184,136,241,48,40,73,5,89,162,130,37,38,74,205,129, +150,228,90,150,102,70,68,179,42,0,0,0,32,0,73,1,32,228,2,3,4,133,164,5,65,178,128,76,99,3,2,89, +4,38,6,137,11,27,232,6,7,8,182,76,23,59,122,146,8,64,80,146,5,120,156,177,81,20,147,96,101,185,22,165, +153,145,145,44,202,0,128,0,8,0,73,1,32,228,2,3,4,197,164,5,65,178,96,76,91,3,2,89,4,42,5,203, +144,177,190,6,7,8,139,67,5,210,6,35,229,1,195,180,50,72,143,201,147,25,42,54,5,155,148,106,89,233,25,18, +204,168,0,0,0,128,0,73,1,32,228,2,3,4,197,164,5,65,178,96,76,91,3,2,89,4,44,5,203,203,27,165, +6,8,40,7,209,130,247,111,146,8,80,12,18,57,71,133,73,12,20,156,2,205,202,181,40,240,148,137,102,84,0,0, +0,64,0,73,1,32,228,2,3,4,197,164,5,65,178,96,76,91,3,2,89,4,58,4,140,20,57,6,77,7,8,22, +209,146,242,35,229,1,195,164,60,66,88,160,138,57,192,44,220,195,82,46,201,72,150,101,0,64,0,4,0,73,1,32, +228,2,3,4,197,164,5,65,178,96,76,91,3,2,89,4,18,2,135,105,6,7,169,8,147,37,228,71,138,1,82,86, +72,152,42,66,96,157,14,171,5,155,152,106,69,217,41,18,204,168,0,0,0,128,0,73,1,32,228,2,3,4,197,164, +5,65,178,96,76,91,3,2,89,4,19,2,135,105,6,7,68,42,94,84,122,8,80,28,59,34,88,76,181,249,80,48, +78,116,11,55,42,212,163,194,82,37,153,80,0,0,1,0,73,1,32,228,2,3,4,197,164,5,128,168,194,209,53,3, +2,144,65,48,42,119,6,7,68,44,94,84,122,8,76,27,71,104,132,135,14,129,18,83,202,185,166,229,26,173,9,200, +150,101,0,64,0,4,0,73,1,32,237,2,3,4,147,9,5,16,38,46,76,83,3,2,89,4,20,2,167,116,6,7, +89,147,15,209,252,8,160,56,36,50,120,8,161,161,245,88,179,115,13,86,132,228,75,50,160,0,0,2,0,73,1,32, +237,2,3,4,147,9,5,16,36,44,80,83,3,2,89,4,17,3,6,19,6,7,81,163,15,223,164,8,152,54,68,27, +58,20,7,160,6,130,170,89,185,118,171,68,39,34,89,149,0,0,0,16,0,73,1,32,237,2,3,4,147,9,5,16, +36,44,80,83,3,2,89,4,17,3,6,19,6,7,81,163,15,211,7,8,21,10,7,19,65,62,6,219,147,106,178,146, +37,153,80,0,0,1,0,73,1,32,237,2,3,4,147,9,5,16,36,44,80,83,3,2,89,4,18,3,6,105,6,7, +54,97,251,248,8,160,82,7,6,136,36,169,150,110,89,177,114,2,204,168,0,0,0,128,0,73,1,32,237,2,3,4, +148,9,5,16,36,42,76,151,3,2,89,4,20,2,230,181,6,7,26,50,253,250,68,8,192,177,211,224,89,185,118,165, +136,11,50,160,0,0,2,0,73,1,32,237,2,3,4,148,9,5,16,36,40,76,151,3,2,89,4,21,2,198,232,6, +134,140,191,76,7,8,72,37,7,142,159,2,205,203,182,44,64,89,149,0,0,0,16,0,73,1,32,237,2,3,4,149, +10,5,22,19,68,214,3,2,65,5,64,169,195,225,6,207,7,8,133,7,18,6,130,202,121,198,244,5,66,196,5,153, +80,0,0,1,0,73,1,32,237,2,3,4,149,10,5,18,18,66,100,186,3,2,200,32,176,20,58,120,41,6,7,159, +8,13,7,6,16,93,78,58,222,5,133,136,11,50,160,0,0,2,0,73,1,32,237,2,3,4,153,10,5,20,28,214, +3,2,65,14,129,211,199,66,6,148,7,158,8,13,7,6,6,84,195,173,232,5,133,100,75,50,160,0,0,2,0,73, +1,16,209,4,3,109,153,10,5,20,28,200,3,2,31,159,3,167,205,133,6,51,7,8,193,7,74,6,71,1,130,2, +65,209,118,72,84,129,101,89,208,0,0,1,0,73,1,16,209,4,3,109,152,10,5,21,27,201,3,2,18,159,3,136, +38,131,6,7,157,8,10,7,81,6,7,20,178,142,39,2,90,107,2,3,42,206,0,128,0,8,0,73,1,16,209,4, +3,109,152,10,5,21,27,204,3,2,18,154,46,40,5,16,141,23,233,6,7,8,58,26,7,6,66,1,32,66,167,2, +90,107,2,3,42,206,0,128,0,8,0,73,1,16,209,4,3,109,151,10,5,22,26,203,3,2,18,153,44,56,21,1, +24,49,6,164,7,8,240,105,7,6,1,8,129,10,149,2,100,89,182,2,3,178,172,232,0,0,0,128,0,73,1,16, +209,4,3,109,150,10,5,33,97,172,3,2,177,41,130,194,192,88,233,100,74,6,37,7,34,8,1,7,157,6,81,214, +36,60,66,165,2,213,66,141,181,2,3,149,103,0,64,0,4,0,73,1,16,209,4,3,109,150,10,5,33,97,172,3, +2,169,41,130,178,224,100,136,153,173,6,7,18,8,34,28,7,6,1,104,10,174,100,42,2,92,112,163,109,2,3,101, +89,208,0,0,1,0,73,1,16,209,4,3,109,149,10,5,33,33,50,99,185,3,2,98,82,229,133,128,218,42,37,6, +41,7,8,19,2,9,6,58,164,157,85,155,40,226,2,81,153,115,26,2,3,202,179,160,0,0,2,0,73,1,16,209, +4,3,109,149,10,5,33,33,50,99,185,3,2,82,82,229,101,128,200,241,51,26,6,7,69,8,66,7,148,6,54,165, +29,85,155,40,226,2,81,153,113,77,2,3,101,89,208,0,0,1,0,73,1,16,209,4,3,109,148,10,5,33,49,66, +99,185,3,2,98,82,197,133,64,168,233,129,158,6,7,8,100,30,146,3,35,147,6,138,89,197,85,178,142,36,2,199, +140,24,214,2,3,85,157,0,0,0,16,0,73,1,16,209,4,3,109,148,10,5,33,49,82,99,185,3,2,98,82,197, +133,0,192,225,67,212,6,164,7,8,192,61,73,6,178,2,35,166,212,177,21,76,133,73,2,179,48,99,2,3,89,86, +116,0,0,0,64,0,73,1,16,209,4,3,109,148,10,5,25,21,202,3,2,146,150,43,40,5,71,12,13,105,6,7, +8,48,15,82,6,76,136,201,3,211,138,104,130,168,66,164,2,217,152,47,151,2,146,160,0,0,2,0,73,1,16,209, +4,3,109,147,10,5,33,49,98,130,185,3,2,82,84,46,88,88,189,16,200,215,6,7,8,46,14,209,31,136,17,62, +67,74,105,65,67,74,161,10,147,2,33,44,240,212,149,0,0,0,16,0,73,1,16,209,4,3,109,147,10,5,26,22, +202,3,2,146,161,82,194,96,96,108,161,202,6,82,7,8,92,30,35,249,1,66,65,18,51,75,91,70,149,66,21,2, +54,208,212,149,0,0,0,16,0,73,1,16,209,4,3,109,146,10,5,35,100,192,160,180,2,137,41,160,146,5,3,227, +90,6,7,75,8,131,196,126,85,37,193,146,68,120,248,218,156,81,83,50,170,144,171,2,104,134,26,25,146,74,0,0, +0,32,0,73,1,16,209,4,3,109,146,10,5,35,102,69,229,2,73,81,11,146,33,113,162,227,124,6,7,8,184,59, +71,197,146,28,144,68,52,76,134,148,226,138,49,133,88,133,73,2,146,148,3,140,232,0,0,0,128,0,73,1,16,209, +4,3,100,133,168,5,141,149,2,99,18,2,132,168,96,141,16,248,213,6,50,7,8,192,233,16,154,57,114,115,37,15, +205,41,228,213,35,42,169,10,182,2,134,26,27,103,0,64,0,4,0,73,1,16,209,4,3,100,133,168,5,141,149,2, +99,18,2,36,165,128,249,113,100,61,6,7,37,8,129,218,20,62,88,160,130,24,41,124,134,148,242,106,57,149,218,2, +134,154,155,103,0,64,0,4,0,73,1,16,209,4,3,100,133,168,5,141,149,2,99,18,2,36,165,128,241,113,116,46, +6,7,8,88,29,35,211,72,207,82,7,96,169,13,21,80,5,146,142,93,74,40,184,153,106,65,12,179,160,0,0,2, +0,73,1,16,209,4,3,100,133,168,5,132,135,75,140,202,2,18,161,146,50,229,165,208,185,6,7,8,96,116,143,205, +132,6,201,82,59,124,134,85,5,40,202,146,133,23,19,3,198,217,208,0,0,1,0,73,1,16,209,4,3,100,133,168, +5,178,192,236,144,212,2,161,41,146,51,37,165,144,249,6,7,8,80,114,143,11,32,23,42,73,8,178,195,106,50,23, +93,137,174,38,91,27,103,0,64,0,4,0,73,1,16,209,4,3,109,162,81,5,26,38,5,134,164,2,201,80,201,25, +130,210,200,122,6,7,74,8,131,167,134,16,22,133,75,8,33,130,212,46,84,193,64,44,116,170,226,93,177,182,116,0, +0,0,64,0,73,1,16,209,4,3,109,162,81,5,26,38,5,134,164,2,201,80,201,25,130,210,200,124,6,7,8,176, +58,120,97,1,104,120,177,29,145,46,52,179,25,93,73,174,38,91,155,103,0,64,0,4,0,73,1,16,209,4,3,109, +162,81,5,26,38,5,134,164,2,73,77,132,5,185,97,116,46,6,7,8,88,29,60,62,126,6,147,7,101,198,150,99, +43,169,53,196,203,115,108,232,0,0,0,128,0,73,1,16,209,4,3,109,162,81,5,26,38,5,134,164,2,73,77,132, +5,185,97,116,46,6,7,8,88,29,60,50,162,172,169,105,162,228,124,132,202,203,169,5,128,154,226,85,185,246,116,0, +0,0,64,0,73,1,16,209,4,3,109,162,81,5,101,0,176,212,2,137,40,152,81,5,11,33,233,6,7,8,40,14, +93,161,181,20,201,113,99,8,108,193,160,151,2,112,125,157,0,0,0,16,0,73,1,16,209,4,3,109,162,81,5,101, +129,217,49,169,2,18,83,64,104,180,178,29,6,50,7,8,128,225,217,162,245,161,162,226,230,80,153,97,197,205,11,174, +37,85,195,140,232,0,0,0,128,0,73,1,16,209,4,3,109,162,81,5,101,129,217,49,169,2,18,81,64,161,5,116, +62,6,7,8,76,27,71,70,139,150,133,203,137,23,155,53,5,56,97,11,174,37,85,195,140,232,0,0,0,128,0,73, +1,16,209,4,3,109,162,81,5,101,129,217,49,169,2,18,81,64,161,5,116,62,6,7,8,76,27,67,101,203,66,197, +197,11,202,155,124,36,100,170,226,93,201,198,116,0,0,0,64,0,73,1,16,209,4,3,109,162,81,5,101,129,217,49, +169,2,18,81,64,161,5,116,62,6,7,8,76,27,67,101,203,68,54,94,84,219,212,38,90,84,201,81,163,11,139,212, +67,236,232,0,0,0,128,0,73,1,16,209,4,3,109,162,81,5,101,129,217,49,169,2,18,81,64,161,5,116,61,6, +7,36,8,129,180,54,92,180,67,133,229,77,190,38,90,76,201,116,3,11,139,228,67,236,232,0,0,0,128,0,73,1, +16,209,4,3,109,162,81,5,100,192,184,204,2,137,40,160,80,5,186,29,6,50,7,8,64,210,25,46,38,86,116,188, +161,199,197,139,88,66,194,134,151,18,24,125,157,0,0,0,16,0,73,1,16,209,4,3,100,133,168,5,178,128,228,160, +196,2,137,41,208,48,94,93,15,6,7,146,8,6,144,201,113,66,162,229,228,204,20,56,244,186,222,202,21,91,202,174, +37,211,206,179,160,0,0,2,0,73,1,16,209,4,3,100,133,168,5,178,128,228,160,196,2,137,41,224,46,96,186,31, +6,7,8,36,13,33,162,211,73,210,6,134,10,28,122,133,139,152,66,170,97,197,196,251,58,0,0,0,32,0,73,1, +16,209,4,3,100,133,168,5,178,128,228,160,196,2,137,41,224,46,96,186,31,6,7,8,36,13,33,146,210,165,70,204, +21,56,244,177,115,5,148,195,171,141,5,14,16,80,0,0,1,0,73,1,16,209,4,3,100,133,168,5,180,67,34,242, +2,36,162,193,5,46,135,6,164,7,8,136,52,134,75,10,149,28,48,132,206,189,48,92,209,32,108,234,227,5,60,206, +0,128,0,8,0,73,1,16,209,4,3,100,133,168,5,141,154,22,145,2,37,22,9,5,99,104,156,6,7,8,136,50, +142,9,147,45,12,10,150,47,34,96,176,21,56,133,148,164,108,234,226,125,157,0,0,0,16,0,73,1,16,210,2,3, +4,217,32,161,5,161,82,8,68,148,92,37,5,142,39,6,7,8,36,13,56,50,74,201,99,134,76,21,56,132,206,21, +66,62,184,136,16,80,0,0,1,0,73,1,16,215,2,3,4,81,32,162,5,22,20,40,76,64,252,80,13,28,78,6, +7,8,72,26,112,84,152,153,97,67,8,104,193,67,136,244,201,113,213,196,77,157,0,0,0,16,0,73,1,16,237,2, +3,4,148,10,5,19,19,70,144,177,48,192,22,56,156,6,7,8,144,52,224,169,42,22,46,66,138,169,35,4,204,153, +58,85,110,58,184,149,102,163,108,240,9,0,0,4,0,73,1,16,237,2,3,4,148,10,5,33,33,69,162,25,38,62, +6,16,137,134,6,7,146,8,32,211,132,52,74,217,48,36,116,137,132,38,97,9,157,42,112,193,131,78,73,165,40,219, +60,9,0,0,1,0,73,1,16,237,2,3,4,149,10,5,33,17,53,162,22,38,25,9,5,83,212,6,201,7,8,3, +78,74,169,8,84,85,16,170,174,133,12,27,56,80,201,149,196,203,99,140,224,0,0,8,0,73,1,16,162,2,203,5, +5,16,184,209,4,219,3,4,26,2,200,58,6,7,72,8,131,78,10,41,4,212,165,152,22,36,96,153,122,26,56,84, +193,213,198,5,80,156,219,56,0,0,2,0,73,1,16,162,2,203,133,5,16,184,223,2,3,4,108,36,20,6,200,7, +8,131,45,148,81,201,169,43,50,44,210,20,47,67,71,10,152,56,184,191,66,132,230,217,192,0,0,16,0,73,1,16, +162,2,203,132,5,136,22,28,237,2,3,4,141,129,100,13,6,7,40,8,140,182,89,71,66,34,166,69,137,23,201,8, +129,147,139,88,138,226,81,155,40,109,156,0,0,1,0,73,1,16,162,2,204,5,5,16,176,231,2,3,4,108,38,20, +6,209,7,8,24,109,10,40,200,135,133,72,151,19,2,103,10,45,133,143,147,28,36,184,149,102,81,201,166,112,0,0, +4,0,73,1,16,162,2,204,4,5,136,21,29,237,2,3,4,133,3,6,7,74,8,35,72,85,70,66,202,106,201,129, +34,233,8,96,116,72,217,114,130,163,68,151,18,172,138,57,52,206,0,0,0,128,0,73,1,16,162,2,204,133,5,16, +168,239,2,3,4,108,40,20,6,209,7,8,26,66,138,42,33,33,82,99,69,71,8,150,150,7,68,77,151,41,42,50, +77,113,40,196,171,131,76,224,0,0,8,0,73,1,16,162,2,203,5,5,104,178,32,80,123,2,3,182,4,21,14,6, +7,8,136,195,68,148,130,138,50,201,12,149,2,101,164,192,177,178,171,97,99,165,151,18,172,8,183,52,206,0,0,0, +128,0,73,1,16,162,2,202,133,5,16,152,154,52,76,155,3,4,108,42,26,6,7,81,8,109,18,82,75,168,173,150, +2,69,178,8,128,80,217,51,166,87,82,107,137,102,166,72,10,4,0,0,1,0,73,1,16,162,2,202,133,5,16,152, +154,52,76,155,3,4,108,42,20,6,209,7,8,109,18,82,75,40,173,151,2,37,180,7,147,8,2,134,201,157,50,186, +150,92,76,40,6,153,192,0,0,16,0,73,1,16,162,2,202,133,5,16,160,162,19,38,219,3,4,11,6,6,7,149, +8,163,70,20,70,204,1,18,196,32,170,84,9,154,68,44,104,153,226,107,137,70,165,74,205,51,128,0,0,32,0,73, +1,16,162,2,202,133,5,16,200,175,2,3,4,108,46,29,6,7,8,22,201,69,41,8,138,25,2,37,105,8,40,5, +13,19,56,81,4,153,242,203,139,164,46,244,203,56,0,0,2,0,73,1,16,162,2,202,133,5,16,168,170,18,38,218, +3,4,11,134,6,7,149,8,178,133,148,53,152,2,101,114,8,160,68,209,85,192,168,161,165,197,218,23,122,101,156,0, +0,1,0,73,1,16,162,2,202,5,5,16,208,191,2,3,4,104,46,20,6,212,7,8,141,19,82,208,136,161,128,34, +86,68,168,176,58,120,85,113,42,187,154,92,76,181,50,206,0,0,0,128,0,73,1,16,162,2,202,5,5,16,208,191, +2,3,4,104,48,26,6,7,86,8,201,85,45,102,64,153,84,8,168,17,50,101,113,40,92,93,113,50,212,203,56,0, +0,2,0,73,1,16,162,2,202,5,5,16,208,191,2,3,4,104,48,20,6,212,7,8,201,53,49,104,179,0,72,168, +137,73,80,34,100,202,226,80,188,162,226,113,169,150,112,0,0,4,0,73,1,16,162,2,202,5,5,16,184,186,17,38, +217,3,4,12,134,6,7,136,193,53,49,104,179,0,80,166,8,88,9,153,38,94,97,119,38,184,156,106,101,156,0,0, +1,0,73,1,16,162,2,202,5,5,16,208,199,2,3,4,100,50,20,6,162,7,20,83,86,139,46,4,202,114,7,8, +192,233,225,85,196,192,162,19,92,73,181,46,248,203,56,0,0,2,0,73,1,16,162,2,202,5,5,16,208,199,2,3, +4,100,76,40,6,40,75,7,68,36,80,73,76,89,96,42,81,8,44,4,140,19,52,96,81,9,174,38,90,148,102,178, +173,32,0,0,8,0,73,1,16,162,2,202,5,5,22,92,97,8,147,108,3,4,136,4,6,160,63,18,41,162,7,202, +168,107,66,162,133,73,229,8,64,137,131,43,137,85,216,162,226,93,17,10,51,89,86,144,0,0,4,0,73,1,16,162, +2,202,5,5,22,92,97,8,147,108,3,4,159,9,6,16,162,7,54,143,196,138,136,147,147,83,142,42,136,65,37,8, +64,154,121,115,37,76,21,92,95,34,26,102,178,173,32,0,0,8,0,73,1,16,162,2,202,5,5,22,92,97,8,147, +108,3,4,158,10,6,36,8,136,81,7,8,140,8,22,17,37,38,167,147,84,86,100,154,8,84,28,187,46,179,24,2, +107,137,50,51,76,214,85,164,0,0,1,0,73,1,16,237,2,3,4,147,10,5,35,104,88,100,161,49,208,166,6,7, +8,80,28,34,61,126,34,90,134,149,22,15,38,35,151,135,151,4,42,228,195,86,202,51,89,86,144,0,0,4,0,73, +1,16,237,2,3,4,147,10,5,35,101,134,10,147,29,10,6,75,8,133,7,234,79,1,151,81,7,101,21,18,128,92, +144,170,81,146,210,199,27,48,229,234,22,122,73,154,202,180,128,0,0,32,0,73,1,16,237,2,3,4,148,10,5,23, +23,42,76,112,41,6,7,152,8,7,143,200,150,158,48,80,142,5,80,11,145,152,73,40,148,76,168,169,163,14,74,164, +54,136,73,154,202,180,128,0,0,32,0,73,1,16,237,2,3,4,148,10,5,23,23,42,76,112,41,6,8,50,20,7, +126,65,15,152,148,7,84,9,1,116,217,7,116,151,102,22,245,149,52,97,201,68,150,138,179,89,86,144,0,0,4,0, +73,1,16,237,2,3,4,148,10,5,33,97,98,164,198,194,6,153,7,8,160,124,153,6,1,224,32,180,162,170,130,25, +34,58,142,72,173,104,84,209,87,37,26,153,102,178,173,32,0,0,8,0,73,1,16,237,2,3,4,148,10,5,33,81, +98,196,198,194,6,147,8,1,7,68,34,37,6,32,135,141,68,7,72,75,37,59,66,203,114,33,101,204,171,146,141,74, +59,38,205,101,90,64,0,0,16,0,73,1,16,237,2,3,4,148,10,5,33,81,98,196,198,130,6,153,7,8,96,116, +132,71,8,131,83,1,192,65,113,69,21,3,36,8,89,49,217,101,184,217,123,226,174,92,17,2,110,201,179,89,86,144, +0,0,4,0,73,1,16,237,2,3,4,149,10,5,33,65,82,196,198,130,6,146,8,161,7,71,136,132,71,144,136,187, +103,0,145,33,69,60,194,116,7,186,115,178,203,105,161,68,44,229,217,16,38,232,155,53,149,105,0,0,0,64,0,73, +1,16,237,2,3,4,150,10,5,20,19,44,76,104,57,6,7,8,80,114,237,11,16,168,217,147,42,114,9,167,22,43, +50,185,34,22,114,241,10,53,40,205,101,90,64,0,0,16,0,73,1,16,237,2,3,4,150,10,5,20,19,44,76,100, +41,6,7,148,8,7,8,240,193,17,68,213,157,10,5,24,33,48,156,233,13,21,145,50,178,64,93,201,81,178,172,214, +85,164,0,0,1,0,73,1,16,237,2,3,4,154,10,5,35,98,36,212,6,149,8,10,7,25,70,126,81,37,104,121, +74,46,157,7,66,201,206,203,21,148,52,165,19,3,14,74,14,21,102,178,173,32,0,0,8,0,73,1,16,237,2,3, +4,154,10,5,45,4,73,185,6,7,8,64,112,142,200,163,29,73,67,227,231,211,145,203,179,104,109,148,3,46,73,142, +150,102,178,173,32,0,0,8,0,73,1,16,237,2,3,4,153,10,5,35,104,34,77,6,202,7,8,3,132,38,138,117, +41,14,143,137,39,59,52,179,182,80,13,46,36,199,139,51,89,86,144,0,0,4,0,73,1,16,237,2,3,4,152,10, +5,35,104,130,4,201,6,202,7,8,3,132,126,120,36,5,128,224,249,116,211,9,206,208,209,83,100,192,186,226,125,154, +202,180,128,0,0,32,0,73,1,16,237,2,3,4,152,10,5,35,104,130,4,201,6,202,7,8,3,132,126,120,36,5, +192,216,249,132,210,233,206,205,21,23,72,42,228,251,53,149,105,0,0,0,64,0,73,1,16,237,2,3,4,151,10,5, +33,33,179,228,200,156,6,7,8,160,56,71,231,130,5,80,13,15,150,76,48,154,237,11,33,27,48,85,201,117,56,179, +53,149,105,0,0,0,64,0,73,1,16,237,2,3,4,151,10,5,33,33,179,228,200,156,6,7,8,160,56,116,252,240, +74,5,1,161,242,233,136,85,49,29,155,41,46,134,85,201,118,165,153,172,171,72,0,0,2,0,73,1,16,237,2,3, +4,150,10,5,33,49,163,196,200,148,6,202,7,8,3,135,101,201,77,39,33,165,8,184,201,148,196,52,180,24,66,42, +228,187,98,204,214,85,164,0,0,1,0,73,1,16,237,2,3,4,149,9,5,16,38,42,88,152,192,105,6,7,8,40, +14,93,191,62,18,5,160,96,120,170,98,21,76,109,29,151,40,56,5,92,151,110,88,129,101,90,64,0,0,16,0,73, +1,16,237,2,3,4,149,10,5,33,65,83,164,200,148,6,202,7,8,131,151,143,207,132,5,168,23,29,44,155,7,66, +201,104,92,164,224,21,114,101,193,70,107,42,210,0,0,0,128,0,73,1,16,237,2,3,4,148,9,5,16,42,44,84, +152,184,82,6,8,88,47,7,31,159,9,5,80,46,58,89,54,7,85,41,29,27,40,50,132,125,201,82,5,149,105,0, +0,0,64,0,73,1,16,237,2,3,4,148,10,5,33,97,98,164,197,194,6,146,8,193,7,120,252,248,74,5,129,113, +210,169,148,7,44,148,134,10,22,100,31,33,247,37,72,22,85,164,0,0,1,0,73,1,16,237,2,3,4,147,9,5, +16,44,48,84,152,184,82,6,8,84,40,7,241,249,240,149,5,2,227,133,82,231,7,82,168,5,77,4,210,41,119,38, +24,147,102,178,173,32,0,0,8,0,73,1,16,237,2,3,4,147,10,5,35,101,134,10,147,23,10,6,74,8,133,7, +218,63,16,9,5,80,54,167,232,6,7,93,40,201,211,104,72,105,204,5,163,18,108,214,85,164,0,0,1,0,73,1, +16,237,2,3,4,146,9,5,16,109,10,140,149,38,46,20,6,149,8,11,7,183,226,33,5,42,5,70,202,165,202,7, +164,163,162,231,11,159,44,7,28,146,120,82,77,101,90,64,0,0,16,0,73,1,16,237,2,3,4,146,10,5,35,101, +65,210,21,38,46,20,6,149,8,10,7,60,126,34,18,5,128,109,79,202,6,7,164,185,66,234,241,67,134,64,227,146, +108,137,179,89,86,144,0,0,4,0,73,1,16,228,2,3,4,133,100,5,65,178,160,233,10,147,23,14,6,7,8,88, +29,60,126,34,18,5,128,84,100,154,74,80,7,226,65,34,211,224,85,201,114,245,161,73,53,149,105,0,0,0,64,0, +73,1,16,228,2,3,4,133,104,5,141,150,7,104,84,152,184,82,6,8,88,47,7,31,136,132,5,160,20,25,40,147, +7,66,136,200,233,12,43,167,192,243,149,161,73,53,149,105,0,0,0,64,0,73,1,16,237,2,3,4,162,81,5,26, +48,15,80,161,49,112,166,6,7,8,84,28,188,126,36,18,5,96,101,80,42,144,210,57,67,10,217,82,227,35,229,156, +150,116,180,66,114,101,154,64,0,0,16,0,146,1,14,2,3,4,218,37,5,17,163,0,245,10,19,24,14,6,7,8, +84,28,187,126,40,18,5,64,72,84,154,50,80,7,234,45,5,12,138,180,88,11,57,40,232,179,252,4,3,179,72,0, +0,2,0,146,1,14,2,3,4,218,37,5,17,163,0,245,10,19,24,14,6,7,8,84,28,163,241,80,145,5,2,66, +132,145,82,7,133,81,93,153,86,138,173,235,50,5,92,148,48,93,254,4,3,89,164,0,0,1,0,146,1,14,2,3, +4,218,37,5,17,163,0,245,10,19,24,13,6,7,37,8,1,202,60,126,108,14,39,45,16,154,41,116,52,44,82,84, +201,160,126,135,28,151,63,4,150,3,105,0,0,0,64,0,146,1,14,2,3,4,218,37,5,17,162,128,80,108,161,49, +144,164,7,8,168,58,191,6,21,3,10,116,6,162,35,72,229,11,147,148,46,46,5,19,206,57,46,127,4,3,44,210, +0,0,0,128,0,146,1,14,2,3,4,218,37,5,22,96,30,162,20,38,50,28,6,7,8,168,57,118,252,232,30,80, +105,6,7,67,168,52,5,48,92,104,13,185,66,232,25,4,3,102,144,0,0,4,0,146,1,14,2,3,4,218,81,5, +25,44,4,199,10,19,25,14,6,7,8,84,28,187,48,70,136,249,180,56,160,19,66,67,168,38,22,87,43,54,5,156, +152,52,78,6,4,3,89,164,0,0,1,0,146,1,14,2,3,4,218,81,5,25,44,4,199,10,19,25,14,6,7,8, +84,28,163,194,196,72,132,70,142,39,161,52,34,199,208,185,58,197,0,179,146,102,140,51,203,4,3,52,128,0,0,32, +0,146,1,14,2,3,4,218,81,5,25,48,15,80,169,49,144,229,6,7,8,129,210,61,68,173,1,16,137,18,155,48, +108,170,154,68,149,11,29,46,76,96,184,224,22,114,96,219,103,26,64,0,0,16,0,146,1,14,2,3,4,218,81,5, +102,1,234,33,82,99,33,6,203,7,8,131,180,71,228,65,180,34,50,217,228,242,132,200,104,164,137,42,204,3,243,64, +93,201,87,71,218,64,0,0,16,0,146,1,14,2,3,4,218,81,5,102,1,234,33,82,99,33,6,164,7,8,192,61, +34,4,197,104,6,109,57,9,31,44,105,209,114,115,96,109,114,52,229,4,154,64,0,0,16,0,146,1,14,2,3,4, +221,5,17,130,224,72,112,169,49,144,166,6,7,8,120,40,7,147,6,233,197,143,54,229,11,19,26,7,231,0,187,146, +174,143,180,128,0,0,32,0,146,1,14,2,3,4,221,5,17,130,224,72,112,169,49,160,231,6,7,8,130,7,147,1, +201,180,6,161,7,66,167,72,80,141,107,66,133,104,216,136,23,114,85,209,246,144,0,0,4,0,146,1,14,2,3,4, +221,5,17,130,224,72,112,169,49,160,210,6,7,8,112,41,7,6,56,152,132,206,21,41,66,196,104,216,200,23,114,85, +209,246,144,0,0,4,0,146,1,14,2,3,4,218,81,5,102,1,234,22,38,52,20,6,205,7,8,131,244,205,6,1, +166,33,35,146,7,165,40,88,141,27,27,3,238,91,68,52,210,0,0,0,128,0,146,1,14,2,3,4,218,81,5,101, +193,226,33,98,99,97,6,76,7,8,200,61,73,6,4,54,152,76,202,26,41,146,5,36,27,3,78,75,181,52,210,0, +0,0,128,0,146,1,14,2,3,4,218,81,5,25,40,4,198,139,19,28,13,6,7,18,8,80,29,17,7,230,139,100, +5,147,40,6,76,188,185,73,34,33,208,52,228,187,83,77,32,0,0,8,0,146,1,14,2,3,4,218,81,5,25,40, +4,198,139,19,28,10,6,37,7,43,8,42,15,137,22,28,75,144,6,85,34,5,202,72,144,143,129,167,37,218,154,105, +0,0,0,64,0,146,1,14,2,3,4,218,37,5,22,84,29,17,2,196,199,194,6,146,7,96,217,210,146,137,68,21, +36,8,64,177,89,34,17,240,44,228,146,144,93,81,52,210,0,0,0,128,0,146,1,14,2,3,4,218,37,5,16,144, +225,65,132,46,76,124,40,6,130,36,132,210,105,6,7,74,8,35,38,140,15,129,103,37,91,148,128,105,164,0,0,1, +0,146,1,14,2,3,4,218,37,5,17,164,66,130,232,92,152,42,4,210,105,6,7,74,8,209,27,71,129,144,44,228, +219,115,77,32,0,0,8,0,146,1,14,2,3,4,218,36,5,136,54,133,69,203,147,5,0,154,69,6,52,7,8,90, +54,136,240,52,6,28,153,110,105,164,0,0,1,0,146,1,14,2,3,4,218,36,5,136,19,29,36,44,92,152,38,4, +211,233,6,7,162,8,48,219,176,232,24,114,101,185,166,144,0,0,4,0,146,1,14,2,3,4,72,90,5,68,11,10, +154,38,31,2,105,180,6,212,7,8,136,218,26,62,248,200,11,42,38,28,146,95,202,191,52,210,0,0,0,128,0,146, +1,14,2,3,4,76,90,5,68,10,10,180,100,152,124,9,165,211,6,7,8,82,52,136,100,251,226,97,7,36,6,10, +79,129,103,36,215,242,207,141,52,128,0,0,32,0,146,1,14,2,3,4,217,48,162,5,19,21,90,24,38,31,2,105, +84,6,209,7,8,109,28,62,253,18,8,92,40,7,253,4,213,66,206,73,181,44,244,211,72,0,0,2,0,146,1,14, +2,3,4,217,48,145,5,2,131,5,201,135,192,162,70,6,7,68,25,66,231,202,145,77,8,3,244,56,92,135,23,18, +109,74,188,54,210,0,0,0,128,0,146,1,14,2,3,4,217,64,162,5,20,24,46,76,60,5,17,244,6,7,70,17, +13,31,126,68,138,8,112,40,7,112,201,197,196,163,82,142,205,180,128,0,0,32,0,146,1,14,2,3,4,217,80,162, +5,19,25,56,76,124,14,162,232,6,7,140,33,163,239,144,137,20,8,240,30,56,109,113,40,208,163,163,109,32,0,0, +8,0,146,1,14,2,3,4,217,96,162,5,18,26,46,76,54,5,80,180,6,7,70,16,217,247,233,7,137,8,4,7, +225,211,75,137,70,101,28,155,105,0,0,0,64,0,146,1,14,2,3,4,217,112,161,5,33,162,228,195,33,5,42,131, +6,162,7,48,134,207,191,76,7,8,76,14,159,52,184,151,86,26,68,54,210,0,0,0,128,0,146,1,14,2,3,4, +217,112,162,5,54,136,92,152,96,11,30,209,6,7,104,133,143,150,34,138,8,5,7,14,32,25,92,74,50,66,110,13, +180,128,0,0,32,0,146,1,14,2,3,4,217,128,162,5,54,133,201,142,129,243,148,6,162,7,208,193,210,196,113,8, +80,16,67,44,228,187,37,162,27,105,0,0,0,64,0,146,1,14,2,3,4,217,144,162,5,208,68,152,217,146,166,168, +6,148,7,89,243,100,15,98,8,192,32,138,89,201,117,121,104,113,164,0,0,1,0,146,1,14,2,3,4,217,48,162, +5,20,21,89,114,99,160,108,242,38,6,7,139,52,100,185,36,8,92,15,163,155,115,5,38,212,227,72,0,0,2,0, +146,1,14,2,3,4,217,48,145,5,2,98,171,50,76,124,9,27,40,26,6,7,44,64,197,6,49,7,8,128,32,196, +157,73,54,230,5,77,101,56,210,0,0,0,128,0,146,1,14,2,3,4,217,48,162,5,21,22,46,76,100,12,26,44, +26,7,6,196,67,6,7,72,8,192,79,7,37,158,92,104,5,116,134,0,0,0,128,0,146,1,14,2,3,4,217,48, +162,5,21,22,46,76,120,8,152,30,14,6,7,8,52,26,7,6,27,79,60,184,208,5,233,12,0,0,1,0,146,1, +14,2,3,4,217,48,162,5,21,23,46,76,112,8,151,23,10,8,197,3,6,7,73,8,112,120,136,80,116,209,12,6, +218,121,117,196,195,163,141,32,0,0,8,0,146,1,14,2,3,4,217,48,162,5,22,23,54,76,134,73,7,195,6,7, +150,8,7,72,143,136,131,100,65,249,128,32,120,113,60,226,227,5,67,196,48,0,0,4,0,146,1,14,2,3,4,76, +90,5,68,27,42,48,104,153,10,145,172,36,20,6,201,7,8,131,104,143,8,129,34,2,68,70,0,137,41,164,194,238, +76,177,54,210,0,0,0,128,0,145,1,13,4,3,22,217,32,162,5,54,84,9,139,201,2,19,24,32,10,6,6,7, +146,8,96,219,164,66,228,104,76,140,169,58,63,54,152,89,113,50,232,219,72,0,0,2,0,145,1,13,4,3,22,217, +32,162,5,54,84,9,11,228,3,2,16,192,240,184,29,51,6,200,7,8,131,40,142,80,201,247,164,73,75,131,183,199, +213,66,75,137,215,69,218,64,0,0,16,0,145,1,13,4,3,22,217,32,162,5,54,84,9,11,235,3,2,32,129,112, +44,161,114,6,7,8,32,203,104,133,76,154,37,68,40,86,125,93,144,2,179,10,106,206,16,192,0,0,16,0,145,1, +13,4,3,22,217,32,162,5,54,84,9,12,107,3,2,32,129,112,44,161,116,6,7,8,70,26,67,169,242,6,36,197, +129,209,226,180,73,2,11,48,191,172,225,12,0,0,1,0,145,1,13,4,3,22,217,32,162,5,54,84,9,12,107,3, +2,32,129,16,56,161,116,6,7,8,91,40,100,249,114,98,192,233,15,150,77,2,57,89,178,20,128,0,0,32,0,145, +1,13,2,3,45,4,146,10,5,35,101,64,144,198,3,2,178,8,21,9,5,117,11,6,7,68,97,4,15,211,6,7, +8,42,14,80,249,108,2,187,146,98,102,136,82,0,0,0,128,0,145,1,13,2,3,117,4,18,10,5,35,101,64,144, +198,3,2,68,152,40,20,5,46,161,104,6,7,180,56,124,177,60,8,168,57,67,229,196,48,179,146,111,205,16,164,0, +0,1,0,145,1,13,2,3,117,4,18,10,5,35,101,64,144,198,3,2,68,152,80,39,5,71,81,11,168,90,6,7, +143,37,150,39,73,8,64,114,32,100,134,76,4,184,100,201,10,36,0,0,1,0,145,1,13,2,3,117,4,18,10,5, +35,101,64,144,198,3,2,68,152,84,39,5,7,16,194,131,6,65,100,174,6,7,8,84,28,159,52,67,9,185,42,221, +163,36,40,144,0,0,4,0,145,1,14,2,3,4,217,48,162,5,54,84,9,12,107,3,2,168,4,84,38,5,198,201, +131,99,193,6,202,7,8,131,148,52,124,203,162,205,219,39,42,85,172,0,0,1,0,145,1,14,2,3,4,217,48,145, +5,6,203,12,173,53,3,2,212,4,44,19,5,35,37,1,248,240,105,6,7,8,40,14,80,209,243,46,139,54,108,156, +169,86,176,0,0,4,0,145,1,14,2,3,4,217,48,145,5,6,203,12,173,53,3,2,212,4,44,20,5,48,50,80, +31,143,133,7,37,8,1,202,76,6,159,50,232,179,102,201,202,149,107,0,0,0,64,0,145,1,14,2,3,4,217,64, +162,5,23,24,90,107,3,2,168,4,92,11,160,163,162,128,252,124,57,6,7,8,80,114,236,201,243,46,139,54,108,156, +169,86,176,0,0,4,0,145,1,10,2,44,168,81,5,11,140,45,36,3,18,4,161,112,46,130,142,138,3,241,240,229, +6,7,8,65,203,179,167,249,2,3,4,165,109,178,100,37,90,192,0,0,16,0,145,1,10,2,44,168,72,5,129,97, +133,164,3,130,84,4,124,12,161,24,76,38,158,62,28,6,7,8,168,57,118,116,254,2,3,73,113,50,212,145,9,86, +176,0,0,4,0,145,1,10,2,44,168,72,5,129,97,133,164,3,130,84,4,48,20,5,50,152,136,216,64,57,6,7, +8,80,114,135,79,228,2,3,157,153,106,72,132,171,88,0,0,2,0,145,1,10,2,44,168,72,5,129,81,133,162,72, +3,37,4,66,64,80,209,100,38,202,3,192,128,114,6,7,8,160,229,14,159,144,4,120,77,177,149,167,2,3,42,214, +0,0,0,128,0,145,1,10,2,44,176,72,5,129,65,101,162,72,3,37,4,67,0,85,5,27,40,15,2,1,6,202, +7,8,131,148,58,126,4,65,225,53,244,187,242,50,178,3,173,96,0,0,8,0,145,1,10,2,44,176,72,5,129,65, +101,162,72,3,37,4,66,96,76,210,22,66,40,14,194,1,6,202,7,8,131,148,58,126,4,65,225,53,244,202,209,2, +149,3,107,0,0,0,64,0,145,1,10,2,44,184,81,5,9,10,147,19,144,3,74,4,132,194,5,109,8,152,54,17, +14,6,7,8,84,28,163,195,135,228,4,30,19,104,101,104,202,2,3,181,128,0,0,32,0,145,1,10,2,44,184,72, +5,129,33,66,68,210,3,9,4,80,152,19,45,44,117,162,96,216,68,57,6,7,8,96,116,240,225,249,4,7,132,213, +115,43,70,2,3,85,172,0,0,1,0,145,1,10,2,44,192,80,5,176,154,72,3,36,4,177,33,99,6,199,154,36, +121,101,1,248,68,52,6,7,149,8,7,79,14,31,144,4,120,77,126,50,247,2,3,42,214,0,0,0,128,0,145,1, +10,2,44,192,80,5,144,153,49,249,3,4,154,4,37,52,112,170,18,19,19,36,44,18,10,6,101,7,8,65,203,195, +135,228,4,30,19,91,12,189,202,2,3,181,128,0,0,32,0,145,1,10,2,44,224,80,5,152,252,3,130,76,4,16, +161,195,5,207,172,152,54,19,14,6,7,8,84,28,187,56,126,4,65,225,53,252,203,156,2,3,171,88,0,0,2,0, +145,1,10,2,44,216,80,5,160,244,3,130,75,4,145,18,3,70,139,18,26,40,31,14,6,7,8,84,28,187,56,126, +4,65,225,54,6,92,229,2,3,90,192,0,0,16,0,145,1,10,2,44,216,80,5,160,244,3,130,76,4,17,146,3, +38,139,17,176,100,152,124,57,6,7,8,80,114,236,225,249,4,7,132,216,25,115,2,149,3,107,0,0,0,64,0,145, +1,10,2,44,208,80,5,168,239,3,2,4,50,72,76,14,155,105,16,152,144,88,57,6,7,8,80,114,236,225,249,4, +7,133,21,99,14,181,2,3,148,109,0,0,0,64,0,145,1,10,2,44,200,80,5,176,231,3,2,4,50,74,84,13, +156,42,66,23,14,6,7,8,84,28,187,56,126,4,65,225,69,172,195,189,2,3,10,54,128,0,0,32,0,145,1,10, +2,44,192,81,5,11,14,115,3,2,4,36,197,64,185,210,232,129,144,229,6,7,8,65,203,179,135,228,4,30,26,102, +218,18,33,66,141,160,0,0,8,0,145,1,10,2,44,192,80,5,176,154,115,3,2,4,68,229,64,177,226,196,109,134, +67,6,7,149,8,7,46,206,31,144,4,120,105,85,109,9,16,161,70,208,0,0,4,0,145,1,10,2,44,184,81,5, +9,138,18,38,237,3,2,4,153,40,44,5,143,150,69,12,135,6,7,8,42,14,93,156,63,4,147,3,118,97,100,36, +66,133,27,64,0,0,16,0,145,1,10,2,44,176,81,5,9,10,148,20,237,3,2,4,161,130,146,224,85,0,178,48, +100,57,6,7,8,80,114,236,225,249,4,7,133,23,242,207,137,16,161,70,208,0,0,4,0,145,1,10,2,44,176,81, +5,12,10,246,2,3,4,144,193,73,80,42,100,193,240,200,114,6,7,8,160,229,217,195,252,4,3,2,219,98,100,37, +91,64,0,0,16,0,145,1,10,2,44,168,81,5,12,139,118,2,3,4,140,132,5,184,20,50,88,248,96,41,6,7, +149,8,7,47,14,31,201,4,3,187,46,177,147,33,42,218,0,0,0,128,0,145,1,14,2,3,4,217,48,162,5,54, +88,96,201,49,98,34,224,85,8,170,64,96,41,6,8,44,23,7,135,15,243,4,3,2,46,26,66,100,37,91,64,0, +0,16,0,145,1,14,2,3,4,217,32,145,5,6,208,168,201,146,98,196,70,0,162,27,69,69,66,225,6,73,8,96, +188,7,56,127,4,3,182,2,105,92,38,66,85,180,0,0,1,0,145,1,14,2,3,4,72,86,5,68,9,14,149,26, +46,76,133,200,203,129,67,37,143,133,195,6,7,150,8,7,79,14,31,230,4,3,2,85,86,144,153,9,86,208,0,0, +4,0,145,1,14,2,3,4,72,86,5,68,9,14,149,26,46,76,133,200,204,1,51,37,143,133,195,6,7,150,8,7, +79,14,31,230,4,3,2,93,50,132,200,74,182,128,0,0,32,0,145,1,14,2,3,4,218,36,5,136,54,76,10,13, +148,38,54,18,5,224,76,201,99,225,112,229,6,7,8,129,211,195,135,251,4,3,2,102,29,176,80,132,171,104,0,0, +2,0,145,1,14,2,3,4,218,36,5,136,54,76,10,13,148,38,54,18,5,224,76,201,83,229,210,150,58,20,6,203, +7,8,3,167,167,15,243,4,3,2,11,250,208,161,9,86,208,0,0,4,0,145,1,14,2,3,4,218,36,5,136,54, +92,30,33,66,99,129,5,46,4,209,138,38,22,72,88,240,82,6,8,92,47,7,78,31,229,4,3,2,150,130,170,145, +134,208,0,0,4,0,145,1,14,2,3,4,218,37,5,17,162,128,80,108,161,49,208,150,5,2,102,77,38,17,22,30, +10,6,75,8,133,7,233,195,253,4,3,2,180,54,240,195,104,0,0,2,0,145,1,14,2,3,4,218,72,5,131,69, +64,152,225,66,99,161,5,46,4,141,22,62,85,68,30,10,6,75,8,5,7,30,156,63,4,219,3,2,56,167,152,109, +0,0,0,64,0,145,1,14,2,3,4,218,72,5,131,69,64,152,225,66,99,193,5,44,4,141,19,64,90,28,80,232, +6,135,83,12,20,75,8,5,7,14,154,230,4,3,2,24,19,3,13,160,0,0,8,0,145,1,14,2,3,4,218,81, +5,25,44,4,199,10,19,31,9,5,80,36,104,217,9,99,250,6,7,155,20,10,6,75,8,5,7,14,154,230,4,3, +2,24,19,3,13,160,0,0,8,0,145,1,14,2,3,4,218,81,5,25,44,4,199,10,147,31,9,5,80,34,104,216, +249,99,250,6,7,144,226,105,130,137,8,96,116,135,76,243,4,3,2,12,9,129,134,208,0,0,4,0,145,1,14,2, +3,4,220,5,136,50,92,9,14,148,38,18,9,5,48,34,104,184,233,165,19,6,7,69,156,77,48,81,8,44,14,144, +233,158,4,3,2,85,137,103,198,27,64,0,0,16,0,145,1,14,2,3,4,220,5,136,50,96,30,161,82,97,48,149, +5,3,67,101,207,232,6,7,179,105,166,74,37,8,129,210,29,51,4,202,3,2,159,42,252,203,104,0,0,2,0,145, +1,14,2,3,4,218,81,5,25,42,4,198,202,147,11,4,5,144,23,23,52,161,232,6,7,180,18,78,166,6,7,8, +88,29,30,49,4,205,3,2,178,67,45,160,0,0,8,0,145,1,14,2,3,4,218,81,5,25,42,4,198,202,147,12, +129,51,76,149,3,7,250,6,7,8,152,48,124,209,58,74,8,128,137,138,2,27,100,134,91,64,0,0,16,0,145,1, +14,2,3,4,218,81,5,25,42,4,198,202,147,12,129,35,75,44,6,15,245,6,7,8,48,130,9,242,6,36,229,193, +217,194,243,107,157,5,14,144,192,0,0,16,0,145,1,14,2,3,4,218,72,5,131,69,129,218,23,38,62,6,205,172, +176,24,63,6,212,7,8,140,161,244,242,8,146,8,224,240,217,113,197,206,5,135,72,96,9,0,8,0,145,1,14,2, +3,4,218,37,5,17,162,64,80,100,177,48,200,17,54,178,192,100,247,6,7,8,68,101,15,39,200,6,147,24,7,134, +203,142,46,116,5,58,67,9,0,0,64,0,145,1,14,2,3,4,218,37,5,17,162,64,80,100,177,48,200,17,54,178, +192,100,247,6,7,8,68,97,164,56,158,253,36,7,16,168,31,45,58,185,208,5,225,12,9,0,1,0,145,1,14,2, +3,4,218,36,5,136,54,80,8,139,150,38,40,6,75,76,144,20,3,196,252,6,7,8,136,50,217,196,55,233,8,44, +7,203,15,46,116,5,56,67,9,0,0,64,0,145,1,14,2,3,4,218,36,5,144,54,72,9,10,152,38,68,42,70, +80,188,201,2,148,32,70,137,6,73,8,16,182,7,135,8,243,7,8,32,244,209,89,229,204,251,104,9,0,2,0,145, +1,14,2,3,4,72,86,5,68,27,40,46,180,44,76,96,37,5,64,192,241,114,3,97,6,73,8,32,182,7,116,140, +176,31,43,60,185,159,109,9,0,0,64,0,145,1,14,2,3,4,72,86,5,72,27,42,5,137,140,132,5,168,29,29, +71,231,2,6,146,8,65,7,71,38,136,223,205,8,1,130,163,235,156,5,147,243,141,160,9,0,8,0,145,1,14,2, +3,4,217,32,146,5,6,202,1,98,99,65,5,42,5,135,10,163,157,10,6,73,8,133,7,29,33,130,39,197,8,207, +148,31,92,201,56,89,198,208,9,0,4,0,145,1,14,2,3,4,217,48,146,5,2,131,37,137,141,132,5,160,22,67, +88,152,82,6,8,80,40,7,237,16,161,2,60,126,100,8,147,28,79,52,236,147,102,16,219,104,9,0,2,0,145,1, +14,2,3,4,217,64,146,5,2,99,71,9,144,185,113,131,5,79,159,10,6,101,7,8,65,203,194,160,228,153,9,130, +83,169,230,157,146,106,194,27,109,9,0,0,64,0,145,1,14,2,3,4,217,80,145,5,2,99,71,73,140,151,22,50, +202,197,67,6,7,151,8,7,111,81,24,70,205,3,242,36,71,83,204,187,38,213,148,54,218,9,0,0,128,0,145,1, +14,2,3,4,217,96,145,5,2,67,103,73,140,151,21,48,88,235,103,204,210,6,8,52,26,7,6,30,73,50,236,170, +196,109,180,9,0,1,0,145,1,14,2,3,4,217,112,145,5,6,209,15,19,28,47,66,71,138,34,24,79,44,126,6, +152,7,8,200,103,7,6,213,179,46,201,179,101,14,54,0,0,64,0,145,1,14,2,3,4,217,64,146,5,18,34,13, +161,98,99,192,92,188,161,178,168,39,6,120,6,7,8,192,104,7,6,125,91,50,236,170,184,113,176,0,2,0,145,1, +14,2,3,4,217,64,161,5,49,52,105,11,19,29,3,7,138,27,42,130,112,102,6,152,7,8,184,98,7,6,10,209, +151,101,85,196,54,216,0,1,0,145,1,14,2,3,4,217,48,145,5,2,130,173,22,38,23,9,5,84,131,3,230,6, +184,6,7,8,168,82,7,6,130,10,209,151,114,5,170,137,182,192,0,8,0,145,1,14,2,3,4,217,48,145,5,2, +162,197,137,133,192,169,242,164,7,6,233,6,7,138,8,6,7,6,130,42,201,135,98,141,128,0,16,0,145,1,14,2, +3,4,217,48,145,5,2,162,230,9,143,129,147,13,33,162,19,3,156,6,7,8,76,41,7,6,18,86,10,187,48,174, +157,108,0,0,128,0,145,1,14,2,3,4,217,48,145,5,2,162,230,9,143,129,147,13,33,178,18,227,180,6,196,7, +8,131,7,6,65,37,96,171,179,10,233,214,192,0,8,0,145,1,14,2,3,4,217,32,146,5,6,202,12,150,38,22, +2,135,218,67,100,38,7,120,6,7,8,136,104,7,6,38,171,149,118,101,93,56,216,0,1,0,145,1,14,2,3,4, +217,32,145,5,6,208,168,201,66,98,195,171,60,18,5,167,77,16,153,81,169,6,7,159,8,10,7,98,6,1,96,85, +217,149,116,227,96,0,4,0,145,1,14,2,3,4,217,32,145,5,6,208,168,201,98,98,197,13,156,2,135,205,146,189, +67,39,105,6,7,73,8,176,126,148,6,72,1,152,21,118,101,93,56,216,0,1,0,145,1,14,2,3,4,217,48,162, +5,54,84,28,154,38,71,196,129,179,32,80,249,210,51,102,233,6,7,73,8,128,122,137,6,72,1,168,24,119,5,44, +167,155,108,0,0,128,0,145,1,14,2,3,4,217,48,162,5,54,84,28,150,38,44,80,80,168,192,241,81,67,64,116, +214,6,7,146,8,224,241,242,36,1,88,10,187,50,175,33,185,96,0,4,0,145,1,14,2,3,4,217,48,145,5,6, +203,12,148,38,46,58,80,168,192,241,113,67,68,103,7,242,6,7,33,8,68,36,42,241,31,140,1,87,102,85,228,55, +44,0,0,128,0,145,1,14,2,3,4,217,48,145,5,6,203,12,149,38,46,80,100,173,16,161,226,232,147,8,10,41, +144,145,6,9,7,16,108,144,63,25,2,174,204,171,200,110,88,0,1,0,145,1,14,2,3,4,217,48,145,5,6,203, +12,148,38,44,58,84,12,15,21,20,54,4,139,206,4,6,152,54,58,5,93,153,87,144,220,176,0,2,0,145,1,14, +2,3,4,217,64,162,5,22,24,54,76,143,201,129,194,212,66,232,135,20,80,248,24,119,5,66,207,77,54,0,0,64, +0,145,1,14,2,3,4,217,64,162,5,22,24,54,76,143,203,129,82,211,104,148,6,54,162,135,192,195,185,5,117,124, +211,96,0,4,0,145,1,14,2,3,4,217,80,162,5,21,22,56,76,146,5,192,84,241,164,61,6,7,70,212,64,248, +21,118,81,177,87,102,155,0,0,32,0,145,1,14,2,3,4,217,80,162,5,21,22,56,76,146,5,96,108,181,10,161, +82,6,7,182,208,209,50,195,160,101,221,5,10,187,52,216,0,1,0,145,1,14,2,3,4,217,96,161,5,65,83,228, +197,192,161,195,40,84,6,172,7,218,136,202,195,160,85,217,102,196,150,179,109,128,0,16,0,145,1,14,2,3,4,217, +112,161,5,49,67,196,210,5,224,100,217,51,137,6,74,7,22,58,88,152,56,5,93,150,106,73,107,54,216,0,1,0, +145,1,14,2,3,4,217,192,161,5,242,105,5,96,40,92,96,226,104,6,7,132,204,21,18,36,104,56,6,93,202,5, +171,198,219,0,0,32,0,145,1,14,2,3,4,217,192,161,5,242,105,5,112,44,92,80,101,19,6,7,68,38,96,194, +3,97,192,50,236,178,200,109,176,0,2,0,145,1,14,2,3,4,217,176,162,5,196,9,165,5,128,177,123,37,78,232, +6,7,132,211,12,31,108,56,7,93,208,5,233,8,0,2,0,145,1,14,2,3,4,217,160,162,5,208,64,154,5,88, +9,152,48,118,6,148,7,70,206,31,108,56,7,29,208,5,241,8,0,2,0,145,1,14,2,3,4,217,144,162,5,54, +130,68,197,129,218,19,48,84,238,6,7,68,15,30,48,150,176,136,28,119,5,67,196,32,0,8,0,145,1,14,2,3, +4,217,128,162,5,54,136,32,76,146,5,192,237,9,152,42,123,6,7,169,8,25,58,120,194,90,194,32,85,216,187,96, +0,4,0,145,1,14,2,3,4,217,128,161,5,33,50,228,198,138,10,129,67,37,69,19,6,7,162,8,217,38,92,96, +233,132,165,132,64,171,178,235,97,226,16,0,4,0,145,1,14,2,3,4,217,112,162,5,18,19,50,76,100,168,184,62, +66,198,211,6,7,162,8,217,38,90,96,227,97,176,54,238,5,134,8,86,80,136,0,1,0,145,1,14,2,3,4,217, +96,162,5,19,20,54,76,96,176,176,61,46,111,6,7,74,8,45,162,101,134,13,24,72,88,68,10,187,44,220,193,10, +202,17,0,0,32,0,145,1,14,2,3,4,217,80,162,5,20,22,46,76,124,40,5,84,217,68,30,6,7,8,136,195, +68,210,206,23,23,83,199,192,171,179,78,10,54,89,66,32,0,4,0,145,1,14,2,3,4,217,64,162,5,21,23,52, +76,124,40,5,96,83,6,7,162,8,48,209,146,82,96,93,5,97,16,42,236,195,149,149,33,89,66,32,0,4,0,145, +1,14,2,3,4,217,48,145,5,2,195,6,73,143,133,5,12,10,116,6,7,8,70,17,179,4,164,192,186,10,194,32, +101,217,55,11,68,41,44,178,141,192,0,16,0,145,1,14,2,3,4,217,32,145,5,6,208,168,201,146,99,225,5,66, +230,180,6,7,145,8,6,156,23,37,70,201,1,116,21,132,64,203,178,110,86,136,82,89,101,27,128,0,32,0,145,1, +14,2,3,4,72,86,5,68,27,42,14,144,169,48,152,21,60,72,215,6,7,8,36,13,56,44,62,136,209,229,60,124, +10,187,48,233,148,41,44,178,141,192,0,16,0,162,1,104,4,3,182,200,133,5,17,178,224,72,102,2,68,152,76,40, +5,88,213,6,50,7,8,64,211,147,4,205,18,3,104,35,224,93,115,42,160,180,133,27,44,163,112,0,4,0,162,1, +104,4,3,182,209,36,5,65,178,224,72,102,2,68,152,76,12,30,164,6,8,152,92,7,147,75,66,130,134,85,18,194, +227,225,2,52,237,164,40,217,101,27,128,0,32,0,162,1,104,4,3,182,209,36,5,65,178,192,76,102,2,68,152,72, +12,158,164,6,8,152,80,7,193,59,68,192,210,16,248,23,92,202,186,108,171,101,148,110,0,0,128,0,162,1,104,4, +3,182,209,40,5,141,23,2,99,82,2,36,199,192,226,5,6,36,8,194,7,142,140,19,182,72,12,41,225,64,150,2, +118,105,178,202,55,0,0,64,0,162,1,104,4,3,182,210,5,68,26,46,4,198,164,2,73,132,128,202,5,6,37,8, +2,7,232,193,59,100,128,210,13,6,22,56,5,215,50,46,205,54,89,70,224,0,8,0,162,1,105,2,3,4,109,36, +5,65,162,224,241,9,147,144,65,32,50,129,6,202,7,8,3,135,69,9,208,169,122,23,65,38,113,99,128,93,115,34, +236,211,101,148,110,0,0,128,0,162,1,116,2,3,4,109,40,5,140,152,7,168,80,157,161,0,52,131,6,76,7,8, +160,56,118,80,157,10,151,162,24,65,32,184,68,176,108,10,174,100,222,26,108,178,141,192,0,16,0,162,1,118,2,3, +4,146,5,68,26,42,4,199,10,147,137,1,132,26,6,74,8,133,7,10,38,162,22,22,48,131,6,162,7,52,54,6, +158,22,110,73,178,202,55,0,0,64,0,162,1,118,2,3,4,146,5,68,26,42,4,199,10,147,137,1,132,26,6,74, +8,133,7,30,18,77,42,82,76,88,184,190,6,7,72,176,104,13,60,44,220,147,101,148,110,0,0,128,1,34,29,2, +3,4,180,74,5,35,69,193,226,22,39,62,6,208,105,6,8,44,23,7,132,202,10,21,20,21,44,47,6,212,7,8, +141,12,129,87,135,19,137,54,89,70,224,0,8,1,34,29,2,3,4,180,74,5,35,69,193,226,22,39,62,6,208,105, +6,8,44,23,7,133,10,17,13,25,38,61,6,212,7,8,68,180,96,11,188,153,5,78,36,216,171,112,0,4,1,34, +29,2,3,4,180,74,5,35,69,193,226,22,39,62,6,208,105,6,8,44,20,7,122,68,160,152,24,50,80,119,6,7, +36,90,216,192,23,120,81,68,45,19,4,3,42,220,0,1,1,34,28,2,3,4,144,173,5,16,144,225,81,147,36,231, +194,5,93,6,6,146,8,225,7,122,84,165,10,154,46,59,6,145,7,44,34,90,46,5,94,20,70,48,227,4,3,42, +220,0,1,1,34,28,2,3,4,144,173,5,17,180,66,163,8,84,156,193,1,98,211,104,52,6,151,8,11,7,211,103, +201,153,40,155,6,148,7,45,23,2,175,10,176,46,162,101,4,3,91,128,0,32,1,34,28,2,3,4,144,173,5,17, +180,66,195,6,137,203,1,242,244,164,6,8,184,81,7,241,100,50,134,139,142,100,6,7,203,95,139,129,119,133,214,162, +112,178,4,3,141,208,0,1,1,34,29,2,3,4,178,65,5,68,44,44,141,152,39,126,108,13,160,37,6,38,8,2, +7,248,186,33,51,101,213,28,6,7,145,97,18,209,96,46,240,202,208,106,96,0,1,1,34,29,2,3,4,178,65,5, +34,5,197,76,19,191,50,7,140,211,6,7,8,46,14,208,186,35,101,0,170,49,67,108,182,232,88,10,188,50,201,180, +53,48,0,0,128,1,34,29,2,3,4,178,97,5,34,5,133,12,147,191,52,7,77,105,6,7,8,44,14,209,241,116, +67,10,186,18,54,80,181,208,176,24,120,89,147,104,106,96,0,1,1,34,29,2,3,4,178,129,5,34,4,133,22,136, +100,157,249,240,40,124,144,83,6,7,8,46,14,208,194,43,101,198,75,26,101,28,187,21,2,175,10,56,42,252,212,192, +0,2,1,34,29,2,3,4,178,161,5,68,42,50,100,158,5,42,4,207,233,6,7,8,44,14,209,241,116,86,203,168, +5,204,145,44,21,2,175,10,55,42,248,212,192,0,2,1,34,29,2,3,4,178,225,5,66,131,70,137,226,5,64,84, +253,6,50,7,8,224,237,31,151,69,46,158,217,99,4,74,197,64,195,195,9,230,155,128,0,32,1,34,29,2,3,4, +179,1,5,66,99,102,137,226,5,96,84,254,6,7,146,8,224,241,241,116,98,194,196,140,22,43,71,162,128,97,225,117, +0,219,112,0,4,1,34,29,2,3,4,178,161,5,36,36,132,135,13,147,207,5,129,34,34,167,249,6,7,8,112,118, +143,139,34,150,22,50,94,229,30,138,1,135,133,212,3,109,192,0,16,1,34,29,2,3,4,178,161,5,66,194,103,9, +231,5,128,145,9,83,220,6,7,8,192,60,124,132,81,76,153,52,94,225,211,225,64,44,240,186,75,40,109,184,0,2, +1,34,29,2,3,4,178,161,5,68,38,40,180,58,78,147,5,64,84,128,161,234,6,102,7,8,1,227,242,197,69,12, +19,46,48,84,40,6,94,22,79,54,220,0,1,1,34,29,2,3,4,178,129,5,68,42,44,179,68,229,139,10,1,145, +226,135,169,6,7,151,8,7,111,144,169,73,98,242,10,168,92,169,240,160,25,120,85,61,102,219,128,0,32,1,34,29, +2,3,4,178,129,5,68,44,46,88,156,104,37,5,81,169,6,7,151,8,7,111,138,148,150,85,72,42,25,41,20,2, +175,12,167,155,34,64,0,4 +}; + +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe pcb64.png + +const unsigned short data_pcb64_png::pal[14] PROGMEM_FAR = +{ +6628,11239,12870,0,10561,1,44405,31727,19017,8484,14696,4193,44373,10597 +}; + +const unsigned char data_pcb64_png::data[665] PROGMEM_FAR = +{ +100,70,98,96,32,0,0,0,2,134,164,6,36,138,0,46,0,0,0,0,32,4,128,0,0,0,0,80,71,1,18,64, +67,75,4,179,75,156,5,38,15,106,2,5,12,33,137,37,3,8,33,154,129,160,4,80,216,178,33,53,202,29,113,250, +161,130,216,234,15,250,92,136,51,46,224,252,52,239,2,116,118,160,5,80,201,173,56,217,214,6,211,116,250,111,146,246, +90,175,44,54,159,163,179,15,155,88,66,126,61,240,107,51,139,235,206,29,62,234,18,246,6,209,47,31,234,95,211,84, +144,195,171,223,210,49,17,68,196,78,99,85,163,121,179,5,7,160,91,73,94,59,229,151,217,57,114,237,6,6,7,8, +208,166,9,88,105,214,87,172,203,178,192,235,163,151,3,164,99,107,93,139,167,93,115,7,235,157,153,134,92,146,143,167, +179,8,200,198,92,188,31,88,113,126,236,250,162,197,129,56,160,39,71,211,92,109,6,17,11,15,198,176,78,149,8,118, +21,115,48,78,153,197,7,160,34,227,178,194,9,208,53,156,127,42,79,248,111,146,230,31,210,151,8,196,58,114,42,3, +16,230,102,15,173,225,25,52,125,100,77,245,67,244,140,216,142,192,253,35,33,107,218,2,6,234,100,77,36,7,214,185, +210,10,4,20,11,242,191,226,93,177,123,146,174,25,174,242,204,101,149,181,140,50,174,199,197,211,225,45,109,96,229,213, +170,243,214,9,3,151,110,31,104,5,22,196,93,196,6,41,180,6,79,170,110,49,157,106,104,136,188,3,134,22,6,103, +32,75,239,92,199,114,93,111,190,45,133,250,86,117,9,217,131,61,212,204,96,121,197,13,205,2,64,141,37,1,93,9, +216,214,20,232,66,236,235,182,4,129,26,72,187,7,112,23,83,163,174,207,184,26,200,174,8,6,81,153,147,47,255,68, +124,21,169,119,179,139,95,171,251,175,103,3,154,5,129,26,73,102,218,215,9,1,127,26,116,255,217,135,30,196,17,90, +6,209,61,22,151,36,25,224,16,184,144,13,154,187,210,164,190,22,168,55,78,35,234,33,89,158,42,6,13,18,216,175, +232,2,17,164,132,160,0,45,4,64,141,36,189,41,136,7,52,1,140,142,6,131,7,8,6,133,9,63,1,64,27,172, +224,19,51,143,218,5,4,105,34,44,3,245,19,181,247,198,224,7,81,136,205,148,125,140,12,4,210,3,245,184,221,42, +201,34,192,250,63,86,242,3,188,62,218,12,37,130,106,13,129,210,143,167,191,59,94,186,216,175,92,195,235,151,96,31, +78,204,80,63,72,184,250,128,125,73,159,71,226,35,232,120,31,79,202,3,200,159,173,192,160,91,31,79,224,29,192,14, +187,249,153,211,116,9,132,187,77,3,240,192,14,147,217,159,128,29,153,1,87,241,129,9,6,129,86,115,53,249,103,131, +6,132,129,191,213,108,101,213,192,11,27,82,164,118,227,227,137,32,115,65,44,101,192,0,16 +}; + +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe screenborder.png + +const unsigned short data_screenborder_png::pal[14] PROGMEM_FAR = +{ +21000,58900,37902,50448,12548,29450,16837,14692,10532,4226,0,52593,18854,35691 +}; + +const unsigned char data_screenborder_png::data[1790] PROGMEM_FAR = +{ +124,2,2,0,33,222,1,197,2,7,2,0,65,2,33,217,3,4,1,201,7,11,2,0,65,2,7,3,33,203,4,7, +3,4,1,204,14,198,16,2,4,33,199,0,2,4,196,16,202,12,196,14,2,0,33,195,5,2,0,196,16,203,5,13, +196,15,8,5,65,1,33,177,0,65,6,8,5,196,21,198,16,65,1,33,175,0,2,6,200,14,65,3,33,173,0,65, +7,198,12,65,3,33,171,0,2,7,196,10,65,2,33,169,4,65,8,198,12,65,2,33,167,4,2,8,198,21,2,2, +33,165,9,3,8,198,30,198,10,198,34,2,2,2,9,33,161,10,2,9,3,8,198,47,202,14,198,51,202,18,198,55, +202,22,198,59,202,26,198,63,202,30,198,67,202,34,198,71,202,38,198,75,202,42,198,79,202,46,198,83,202,50,198,87, +202,54,198,91,202,58,198,95,202,62,198,99,202,66,198,103,202,70,198,107,202,74,198,111,202,78,198,115,202,82,198,119, +202,86,198,123,202,90,198,127,202,94,198,128,131,202,99,198,128,136,202,104,198,128,141,202,109,198,128,146,202,114,198,128, +151,202,119,198,128,156,202,124,198,128,161,202,128,129,198,128,167,202,128,135,198,128,173,202,128,141,198,128,179,202,128,147, +198,128,185,202,128,153,198,128,191,202,128,159,198,128,197,202,128,165,198,128,203,202,128,171,198,128,209,202,128,177,198,128, +215,202,128,183,198,128,221,202,128,189,198,128,227,202,128,195,198,128,233,202,128,201,198,128,239,202,128,207,198,128,245,202, +128,213,198,128,251,202,128,219,198,129,1,202,128,225,198,129,7,202,128,231,198,129,13,202,128,237,198,129,19,202,128,243, +198,129,25,202,128,249,198,129,31,202,128,255,198,129,37,202,129,5,198,129,43,202,129,11,198,129,49,202,129,17,198,129, +55,202,129,23,198,129,61,202,129,29,198,129,67,202,129,35,198,129,73,202,129,41,198,129,79,202,129,47,198,129,85,202, +129,53,198,129,91,202,129,59,198,129,97,202,129,65,198,129,103,202,129,71,198,129,109,202,129,77,198,129,115,202,129,83, +198,129,121,202,129,89,198,129,127,202,129,95,198,129,133,202,129,101,198,129,139,202,129,107,198,129,145,202,129,113,198,129, +151,202,129,119,198,129,157,202,129,125,198,129,163,202,129,131,198,129,169,202,129,137,198,129,175,202,129,143,198,129,181,202, +129,149,198,129,187,202,129,155,198,129,193,202,129,161,198,129,199,202,129,167,198,129,205,202,129,173,198,129,211,202,129,179, +198,129,217,202,129,185,198,129,223,202,129,191,198,129,229,202,129,197,198,129,235,202,129,203,198,129,241,202,129,209,198,129, +247,202,129,215,198,129,253,202,129,221,198,130,3,202,129,227,198,130,9,202,129,233,198,130,15,202,129,239,198,130,21,202, +129,245,198,130,27,202,129,251,198,130,33,202,130,1,198,130,39,202,130,7,198,130,45,202,130,13,198,130,51,202,130,19, +198,130,57,202,130,25,198,130,63,202,130,31,198,130,69,202,130,37,198,130,75,202,130,43,198,130,81,202,130,49,198,130, +87,202,130,55,198,130,93,202,130,61,198,130,99,202,130,67,198,130,105,202,130,73,198,130,111,202,130,79,198,130,117,202, +130,85,198,130,123,202,130,91,198,130,129,202,130,97,198,130,135,202,130,103,198,130,141,202,130,109,198,130,147,202,130,115, +198,130,153,202,130,121,198,130,159,202,130,127,198,130,165,202,130,133,198,130,171,202,130,139,198,130,177,202,130,145,198,130, +183,202,130,151,198,130,189,202,130,157,198,130,195,202,130,163,198,130,201,202,130,169,198,130,207,202,130,175,198,130,213,202, +130,181,198,130,219,202,130,187,198,130,225,202,130,193,198,130,231,202,130,199,198,130,237,202,130,205,198,130,243,202,130,211, +198,130,249,202,130,217,198,130,255,202,130,223,198,131,5,202,130,229,198,131,11,202,130,235,198,131,17,202,130,241,198,131, +23,202,130,247,198,131,29,202,130,253,198,131,35,202,131,3,198,131,41,202,131,9,198,131,47,202,131,15,198,131,53,202, +131,21,198,131,59,202,131,27,198,131,65,202,131,33,198,131,71,202,131,39,198,131,77,202,131,45,198,131,83,202,131,51, +198,131,89,202,131,57,198,131,95,202,131,63,198,131,101,202,131,69,198,131,107,202,131,75,198,131,113,202,131,81,198,131, +119,202,131,87,198,131,125,202,131,93,198,131,131,202,131,99,198,131,137,202,131,105,198,131,143,202,131,111,198,131,149,202, +131,117,198,131,155,202,131,123,198,131,161,202,131,129,198,131,167,202,131,135,198,131,173,202,131,141,198,131,179,202,131,147, +198,131,185,202,131,153,198,131,191,202,131,159,198,131,197,202,131,165,198,131,203,202,131,171,198,131,209,202,131,177,198,131, +215,202,131,183,198,131,221,202,131,189,198,131,227,202,131,195,198,131,233,202,131,201,198,131,239,202,131,207,198,131,245,202, +131,213,198,131,251,202,131,219,198,132,1,202,131,225,198,132,7,202,131,231,198,132,13,202,131,237,198,132,19,202,131,243, +198,132,25,202,131,249,198,132,31,202,131,255,198,132,37,202,132,5,198,132,43,202,132,11,198,132,49,202,132,17,198,132, +55,202,132,23,198,132,61,202,132,29,198,132,67,202,132,35,198,132,73,202,132,41,198,132,79,202,132,47,198,132,85,202, +132,53,198,132,91,202,132,59,198,132,97,202,132,65,198,132,103,202,132,71,198,132,109,202,132,77,198,132,115,202,132,83, +198,132,121,202,132,89,198,132,127,202,132,95,198,132,133,202,132,101,198,132,139,202,132,107,198,132,145,202,132,113,198,132, +151,202,132,119,198,132,157,202,132,125,198,132,163,202,132,131,198,132,169,202,132,137,198,132,175,202,132,143,198,132,181,202, +132,149,198,132,187,202,132,155,198,132,193,202,132,161,198,132,199,202,132,167,198,132,205,202,132,173,198,132,211,202,132,179, +198,132,217,202,132,185,198,132,223,202,132,191,198,132,229,202,132,197,198,132,235,202,132,203,198,132,241,202,132,209,198,132, +247,202,132,215,198,132,253,202,132,221,198,133,3,202,132,227,198,133,9,202,132,233,198,133,15,202,132,239,198,133,21,202, +132,245,198,133,27,202,132,251,198,133,33,202,133,1,198,133,39,202,133,7,198,133,45,202,133,13,198,133,51,202,133,19, +198,133,57,202,133,25,198,133,63,202,133,31,198,133,69,202,133,37,198,133,75,202,133,43,198,133,81,202,133,49,198,133, +87,202,133,55,198,133,93,202,133,61,198,133,99,202,133,67,198,133,105,202,133,73,198,133,111,202,133,79,198,133,117,202, +133,85,198,133,123,202,133,91,198,133,129,202,133,97,198,133,135,202,133,103,198,133,141,202,133,109,198,133,147,202,133,115, +198,133,153,202,133,121,198,133,159,202,133,127,198,133,165,202,133,133,198,133,171,202,133,139,198,133,177,202,133,145,198,133, +183,202,133,151,198,133,189,202,133,157,198,133,195,202,133,163,198,133,201,202,133,169,198,133,207,202,133,175,198,133,213,202, +133,181,198,133,219,202,133,187,198,133,225,202,133,193,198,133,231,202,133,199,198,133,237,202,133,205,198,133,243,202,133,211, +198,133,249,198,133,230,198,133,255,198,133,236,198,134,5,198,133,242,198,134,11,65,2,33,167,0,2,8,198,134,21,33, +169,0,65,8,196,134,29,196,134,35,198,134,53,33,173,2,65,7,196,134,61,33,175,2,2,6,198,134,80,33,177,2, +198,134,88,196,134,100,33,179,2,196,134,95,196,134,136,65,0,33,197,3,65,0,196,134,139,196,134,149,33,199,3,196, +134,148,2,0,65,2,7,3,65,4,33,201,3,65,4,196,134,177,2,0,65,2,196,134,184,33,210,1,200,11,2,0, +65,2,7,3,33,214,11,198,134,222,4,3,197,9,7,2,0,65,12,33,221,13,2,0,33,222,4,37,160,0 +}; + +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe -p exo sideup.png + +const unsigned short data_sideup_png::pal[2] PROGMEM_FAR = +{ +0,65535 +}; + +const unsigned char data_sideup_png::data[280] PROGMEM_FAR = +{ +136,168,193,66,70,138,195,80,6,72,32,138,130,100,128,14,0,0,0,0,0,0,0,0,0,0,0,2,143,17,1,208, +0,124,4,40,31,250,39,212,63,169,128,173,67,250,244,143,168,127,94,145,245,15,235,210,62,161,253,122,71,212,63,174, +200,250,135,245,240,62,161,237,34,7,212,59,165,36,125,67,250,173,86,76,79,168,127,86,230,36,125,67,218,191,51,35, +234,28,214,57,185,31,80,144,63,250,156,55,62,161,127,0,177,14,146,32,80,176,55,93,162,132,10,25,6,248,112,160, +0,30,2,133,20,1,129,194,194,141,135,48,218,0,60,222,229,97,219,212,63,203,24,216,1,28,204,99,38,76,42,131, +220,45,246,198,98,23,105,49,204,3,78,167,186,1,223,56,227,105,6,57,117,63,200,111,184,222,215,249,242,207,114,27, +252,225,13,241,246,196,50,45,133,185,130,197,220,219,114,230,78,115,255,128,57,200,46,64,50,100,10,188,223,242,123,192, +145,230,226,67,28,132,227,10,198,70,249,12,255,33,139,121,112,111,254,57,205,176,188,216,242,90,40,97,207,192,48,216, +119,144,199,252,252,3,182,174,104,203,205,200,247,18,217,32,200,0,211,152,1,240,0,2 +}; + +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe -b 32 1 -p tsc tiles.gif + +const unsigned short data_tiles_gif::pal[49] PROGMEM_FAR = +{ +854,690,822,756,329,526,2145,559,34202,34025,657,2737,17266,6701,27863,17298,10926,591,98,2770,2672,2573,361,25717,19443,558,624,460,427,493,723,592, +525,625,63488,64495,0,50712,459,394,1570,32753,623,492,15152,13104,17265,13072,8846 +}; + +const unsigned char data_tiles_gif::data[4535] PROGMEM_FAR = +{ +96,1,0,1,6,5,3,5,4,6,7,10,10,7,3,7,3,7,1,5,1,5,1,8,1,10,10,3,7,7,6,3, +5,5,1,1,3,1,1,1,1,4,3,1,1,1,3,1,1,1,1,4,3,1,1,3,3,1,1,1,2,4,5,1, +3,1,1,1,6,5,1,1,7,3,1,1,1,1,1,1,1,1,8,6,3,1,3,5,9,8,5,3,5,6,4,3, +1,4,1,1,5,5,3,6,7,6,7,6,8,1,1,6,1,1,1,1,1,1,4,1,1,1,3,4,7,6,5,3, +6,5,1,1,5,5,4,6,4,6,7,4,4,5,5,6,7,6,6,6,7,6,7,5,3,5,8,6,5,6,4,4, +3,3,1,1,1,4,4,5,7,7,6,8,6,3,6,8,6,6,1,1,5,7,7,6,7,6,6,9,8,9,7,8, +8,5,1,1,6,9,6,9,9,9,8,9,8,9,7,6,6,6,1,1,5,9,8,5,6,7,5,8,3,5,4,7, +1,2,1,1,1,1,1,5,6,5,7,6,5,7,6,6,5,4,4,5,6,4,5,3,5,5,5,5,7,7,8,7, +6,4,1,1,4,4,6,7,13,10,11,6,5,4,5,5,5,3,4,8,12,10,13,5,3,5,5,8,6,10,6,7, +8,7,1,1,4,4,4,4,12,9,10,5,5,4,6,4,3,1,3,14,13,11,3,1,5,7,7,8,7,8,5,5, +7,5,1,1,5,5,1,7,10,14,6,8,1,1,1,1,1,1,1,1,1,1,1,8,5,6,7,4,4,1,7,6, +4,3,1,0,0,3,0,15,0,25,0,31,0,41,0,52,0,64,0,78,0,102,0,122,0,136,0,142,0,156,0,162, +0,176,0,178,0,189,0,191,0,201,0,203,0,220,0,222,0,242,0,10,1,17,1,31,1,45,1,57,1,63,1,73, +1,83,1,86,1,89,1,95,1,97,1,100,1,103,1,105,1,113,1,119,1,122,1,125,1,127,1,133,1,135,1,137, +1,140,1,142,1,151,1,157,1,159,1,162,1,168,1,174,1,176,1,178,1,181,1,185,1,194,1,204,1,207,1,213, +1,215,1,218,1,221,1,233,1,243,1,246,1,249,1,7,2,13,2,16,2,19,2,22,2,25,2,28,2,31,2,34, +2,37,2,53,2,65,2,71,2,74,2,80,2,90,2,108,2,124,2,134,2,140,2,150,2,164,2,173,2,179,2,182, +2,191,2,194,2,197,2,208,2,219,2,225,2,237,2,251,2,8,3,22,3,34,3,53,3,55,3,57,3,69,3,71, +3,73,3,75,3,77,3,79,3,81,3,89,3,91,3,93,3,95,3,101,3,109,3,123,3,136,3,147,3,153,3,165, +3,175,3,178,3,181,3,191,3,201,3,209,3,221,3,230,3,242,3,0,4,8,4,16,4,26,4,36,4,48,4,62, +4,74,4,86,4,98,4,112,4,124,4,138,4,148,4,155,4,165,4,181,4,193,4,203,4,215,4,223,4,232,4,239, +4,245,4,248,4,251,4,253,4,5,5,13,5,23,5,37,5,51,5,63,5,80,5,92,5,98,5,111,5,127,5,139, +5,151,5,154,5,157,5,167,5,181,5,195,5,207,5,221,5,233,5,246,5,8,6,24,6,43,6,57,6,73,6,89, +6,99,6,102,6,105,6,119,6,138,6,151,6,170,6,189,6,208,6,224,6,243,6,4,7,22,7,37,7,51,7,64, +7,76,7,79,7,82,7,92,7,111,7,128,7,139,7,152,7,167,7,180,7,197,7,204,7,216,7,224,7,238,7,240, +7,245,7,248,7,251,7,254,7,1,8,4,8,14,8,26,8,36,8,50,8,62,8,73,8,87,8,99,8,111,8,122, +8,130,8,139,8,149,8,161,8,169,8,179,8,186,8,196,8,206,8,217,8,228,8,243,8,1,9,17,9,31,9,43, +9,52,9,55,9,58,9,67,9,75,9,87,9,102,9,131,9,151,9,175,9,187,9,197,9,205,9,215,9,226,9,236, +9,243,9,254,9,20,10,50,10,72,10,99,10,109,10,115,10,126,10,137,10,153,10,165,10,185,10,198,10,212,10,228, +10,242,10,245,10,248,10,1,11,10,11,19,11,28,11,56,11,75,11,95,11,106,11,116,11,125,11,137,11,146,11,152, +11,155,11,162,11,191,11,221,11,245,11,251,11,254,11,8,12,22,12,36,12,52,12,66,12,82,12,93,12,103,12,117, +12,127,12,130,12,133,12,143,12,153,12,156,12,170,12,191,12,223,12,241,12,4,13,6,13,8,13,10,13,12,13,14, +13,16,13,18,13,20,13,22,13,24,13,26,13,45,13,56,13,71,13,85,13,94,13,102,13,105,13,119,13,131,13,139, +13,145,13,32,32,0,65,0,9,1,7,2,8,3,6,1,65,4,65,0,7,3,198,13,2,1,65,4,65,0,3,3, +28,0,134,6,196,31,3,1,15,5,65,4,68,0,3,1,0,7,1,20,5,65,4,65,0,2,1,65,0,2,1,25, +5,65,4,65,0,2,1,65,0,3,5,21,6,3,7,65,4,200,15,68,6,8,6,9,3,6,3,9,3,6,3,9, +3,6,66,8,6,3,7,65,4,65,0,2,1,65,0,3,7,2,6,2,9,202,30,2,6,3,7,65,4,200,21,65, +6,196,15,2,9,65,6,3,7,65,4,202,15,3,5,65,4,196,21,65,9,202,62,3,9,65,6,3,5,65,4,202, +15,3,7,65,4,202,61,2,6,200,80,3,9,2,6,3,7,65,4,206,15,202,77,65,6,197,2,93,3,7,65,4, +202,12,204,90,196,50,65,6,3,7,65,4,202,11,65,0,2,1,65,0,3,5,196,32,66,9,6,3,7,65,4,208, +18,65,0,2,1,65,0,3,5,2,6,196,89,65,9,2,6,3,7,65,4,65,0,2,1,65,0,3,5,66,6,8, +4,6,200,128,169,67,6,8,6,3,1,65,4,202,128,203,3,1,65,4,65,0,2,1,65,0,10,5,14,1,3,5, +65,4,65,0,2,1,65,0,6,5,12,1,9,5,65,4,65,0,2,3,65,0,8,1,19,5,65,4,65,0,3,3, +28,4,134,6,7,3,9,10,12,5,65,4,134,16,65,3,12,10,15,5,65,4,32,32,4,32,32,0,65,0,30,11, +65,4,198,7,32,32,8,32,32,12,194,4,4,12,3,8,3,13,22,12,3,12,198,9,65,12,32,32,13,32,32,8, +194,26,17,12,3,13,12,12,194,34,194,17,32,32,8,194,41,10,12,2,14,18,12,66,13,12,29,12,2,13,65,12, +194,39,32,32,8,8,12,196,36,9,12,8,12,2,13,22,12,194,75,194,58,32,32,8,23,12,9,15,26,12,66,15, +12,3,8,65,15,18,12,3,15,5,12,3,8,3,15,32,32,16,65,17,30,4,65,18,198,125,32,32,4,32,32,0, +65,0,5,19,6,20,7,21,12,20,65,4,65,0,2,19,198,13,3,20,65,4,32,32,8,32,32,12,5,12,2,13, +65,22,17,12,2,8,2,13,3,12,3,12,198,15,9,12,32,32,12,32,32,13,32,32,8,32,32,12,32,32,12,32, +32,12,32,32,13,32,32,8,200,37,7,12,5,13,2,15,65,12,3,15,65,12,3,15,18,12,3,22,2,13,2,15, +65,12,6,15,19,12,198,13,6,15,32,32,13,9,8,8,22,15,8,8,12,2,13,5,22,4,13,13,12,9,12,3, +13,65,22,2,13,2,22,2,13,5,12,4,22,4,12,10,12,2,22,65,13,2,22,5,13,3,12,3,22,6,12,10, +13,3,22,65,16,6,22,12,13,9,8,6,22,17,8,8,12,3,13,3,22,2,13,16,12,8,12,65,13,2,22,67, +13,22,13,2,22,16,12,196,57,2,22,196,129,40,3,12,196,50,198,64,11,13,32,32,4,198,128,218,4,20,15,21, +65,4,32,32,4,32,32,0,196,129,177,5,5,12,10,3,3,65,4,196,129,188,2,5,9,10,9,3,65,4,28,0, +3,3,65,4,196,29,4,5,8,1,65,4,2,3,65,4,65,0,9,5,12,1,6,5,65,4,2,1,65,4,196,130, +219,5,1,10,5,65,4,2,1,65,4,65,0,3,1,21,23,3,5,65,4,2,1,65,4,65,0,3,1,65,23,19, +24,65,16,200,19,65,0,3,7,66,23,24,2,23,2,16,197,2,6,66,23,24,196,22,208,20,208,22,206,24,65,16, +3,7,65,4,2,1,65,4,204,13,204,15,204,17,204,19,204,21,204,23,65,0,3,5,202,48,202,29,200,9,204,35, +204,37,65,0,3,7,200,74,65,0,3,7,21,16,200,94,65,0,5,7,20,5,2,1,65,4,2,1,65,4,196,128, +155,65,5,7,1,65,4,2,3,65,4,196,128,196,12,1,65,4,2,3,65,4,28,4,3,3,65,4,65,0,8,1, +8,3,7,2,7,3,65,4,65,0,6,1,196,13,9,1,65,4,32,32,4,32,32,0,65,0,10,25,14,26,6,27, +65,4,65,0,7,25,13,1,10,27,65,4,65,0,22,26,8,28,65,4,65,0,4,26,5,5,10,26,11,28,65,4, +196,129,14,2,5,6,1,196,32,65,0,2,29,10,27,7,1,11,27,65,4,65,0,6,29,4,28,7,17,7,28,6, +7,65,4,196,15,7,29,17,7,65,4,196,23,3,29,21,17,65,4,65,0,2,26,20,17,8,26,65,4,65,0,3, +26,17,17,10,10,65,4,65,0,4,26,12,17,6,10,8,30,65,4,65,0,5,26,9,17,6,10,4,30,6,1,65, +4,65,0,5,26,6,17,196,27,5,30,65,4,65,0,5,26,4,17,5,10,16,30,65,4,198,25,134,58,8,10,6, +30,2,7,65,4,198,37,65,17,5,29,6,31,4,30,3,7,65,4,65,0,5,26,11,25,4,10,10,7,65,4,65, +0,8,26,4,29,3,25,6,10,9,7,65,4,65,0,5,26,5,29,6,25,198,29,198,128,133,8,17,65,4,65,0, +4,17,12,32,14,17,65,4,65,0,3,26,4,32,5,27,7,5,4,27,7,17,65,4,65,0,4,26,5,32,12,27, +9,17,65,4,65,0,5,26,7,25,196,25,196,25,65,0,6,26,8,25,5,27,11,17,65,4,196,13,65,26,23,17, +65,4,196,100,196,128,198,2,17,65,4,198,128,185,16,17,65,4,65,0,30,17,65,4,32,32,4,32,32,0,198,13, +65,0,27,17,3,26,65,4,65,0,19,17,11,26,65,4,65,0,14,17,9,25,7,26,65,4,65,0,11,17,5,27, +4,29,4,25,6,26,65,4,65,0,14,7,4,27,4,29,3,25,5,26,65,4,65,0,9,7,12,27,5,29,4,26, +65,4,65,0,7,7,4,27,196,128,153,65,27,4,29,3,26,65,4,65,0,9,25,10,5,7,29,4,17,65,4,196, +56,16,7,65,4,196,36,3,17,4,10,6,25,196,50,66,26,4,134,48,3,7,4,17,6,10,3,25,4,29,8,17, +65,4,198,30,3,25,3,29,5,17,5,26,65,4,134,76,65,7,4,30,18,17,5,26,65,4,32,32,4,32,32,0, +65,0,13,30,14,10,3,26,65,4,65,0,6,1,4,30,6,33,9,17,5,26,65,4,65,0,5,1,3,30,196,15, +3,17,4,26,65,4,65,0,3,7,7,33,17,17,3,26,65,4,65,0,196,97,15,17,3,29,3,17,2,26,65,4, +65,0,6,7,196,15,2,29,4,17,65,4,196,128,197,3,7,5,29,4,27,4,29,65,4,65,0,2,7,5,27,7, +28,5,17,65,7,8,27,2,29,65,4,65,0,5,27,4,28,3,27,4,1,10,5,4,27,65,4,65,0,3,27,5, +28,3,27,3,1,196,129,121,65,5,3,27,65,4,65,0,7,28,3,27,3,1,13,17,4,26,65,4,65,0,6,28, +3,27,6,17,2,1,65,10,12,26,65,4,65,0,196,64,65,27,4,17,9,1,2,10,7,25,65,4,65,0,7,27, +13,26,10,25,65,4,32,32,4,32,32,0,196,130,181,4,26,66,0,4,8,26,6,27,65,4,65,0,7,25,7,1, +66,0,4,4,1,3,17,4,27,3,28,65,4,65,0,14,26,66,0,4,6,17,196,122,65,4,65,0,4,26,10,5, +66,0,4,2,17,3,1,3,27,6,28,65,4,65,0,11,29,3,27,66,0,4,4,1,3,27,4,28,3,27,65,4, +65,0,4,27,10,29,66,0,4,3,1,3,27,3,28,5,27,65,4,65,0,6,29,4,27,4,17,134,27,11,27,2, +7,65,4,65,0,4,29,4,27,3,29,3,17,66,0,4,2,17,12,7,65,4,66,0,26,3,7,5,29,5,17,196, +111,2,17,6,7,65,4,65,0,2,26,3,7,3,29,6,17,196,19,3,17,3,7,65,4,65,0,3,26,11,7,66, +0,4,4,17,10,10,65,4,198,130,246,66,0,4,3,10,6,30,5,1,65,4,65,0,8,26,6,17,66,0,4,14, +30,65,4,65,0,9,26,5,17,134,30,13,3,65,4,32,32,4,32,32,0,196,46,7,31,4,30,3,7,65,4,65, +0,5,26,5,17,4,29,66,0,4,4,10,6,31,4,7,65,4,65,0,8,17,4,29,2,25,66,0,4,5,10,9, +31,65,4,198,130,247,4,25,198,34,4,31,65,4,65,0,2,26,12,17,196,128,132,3,17,65,4,65,0,4,17,10, +5,66,0,4,10,7,4,17,65,4,200,131,3,2,5,66,0,4,3,7,198,131,3,65,0,4,26,5,32,5,27,66, +0,4,5,27,9,17,65,4,198,131,5,2,27,198,58,198,131,2,66,0,4,3,27,11,17,65,4,65,0,7,26,7, +25,198,78,65,0,11,26,3,17,134,78,5,17,8,7,65,4,204,100,196,130,228,198,99,32,32,4,32,32,0,198,131, +13,198,131,16,198,131,19,65,0,2,17,6,1,22,17,65,4,65,0,5,33,10,1,11,33,4,17,65,4,65,0,13, +33,11,1,6,33,65,4,65,0,2,17,5,5,15,33,6,1,2,17,65,4,65,0,5,17,9,5,13,33,3,1,65, +4,196,131,40,196,28,5,5,7,33,65,4,65,0,3,17,11,1,8,17,3,5,5,33,65,4,65,0,5,5,6,33, +13,1,6,29,65,4,134,12,2,5,5,26,10,33,11,17,65,4,196,133,160,65,5,11,33,6,1,65,4,134,35,10, +26,11,5,196,60,196,131,126,134,95,8,5,65,4,65,0,3,26,4,5,23,26,65,4,65,0,8,1,65,7,2,25, +19,17,65,4,134,74,2,5,7,1,196,11,65,0,12,33,15,1,3,26,65,4,196,132,235,8,26,65,4,65,0,196, +110,2,5,17,33,65,4,65,0,10,17,10,5,10,26,65,4,196,131,194,3,17,5,30,8,26,65,4,196,131,205,5, +30,5,10,6,26,65,4,196,131,206,65,17,4,30,3,10,7,5,4,26,65,4,65,0,8,17,5,30,196,15,4,5, +3,17,65,4,65,0,5,17,196,29,65,10,5,5,4,17,8,25,65,4,65,0,3,17,7,10,6,5,7,17,7,25, +65,4,65,0,6,10,7,5,12,17,5,25,65,4,196,136,23,65,5,19,17,65,4,32,32,4,32,32,0,196,134,64, +65,1,12,31,65,4,196,37,16,10,8,31,65,4,65,0,2,10,21,0,4,10,3,17,65,4,65,0,2,10,196,132, +87,65,4,3,10,4,17,65,4,65,0,2,10,66,0,17,2,34,2,35,65,34,2,17,67,35,34,17,134,4,5,17, +65,4,7,17,65,4,65,0,2,10,65,0,8,17,2,34,65,17,2,34,6,17,65,4,198,69,65,0,2,10,67,0, +17,34,2,35,196,19,7,17,65,4,2,10,3,17,2,5,65,4,200,45,200,61,65,10,3,17,3,5,65,4,206,86, +202,51,3,17,4,5,65,4,200,111,2,17,5,5,65,4,65,0,2,10,21,4,7,5,65,4,66,0,10,12,7,7, +17,10,5,65,4,65,0,9,10,9,31,12,5,65,4,198,128,162,7,31,65,4,67,0,10,26,196,128,157,7,7,65, +4,65,0,2,10,66,0,17,13,36,70,17,0,4,0,17,4,2,7,5,31,65,4,65,0,2,10,68,0,17,36,37, +2,36,2,37,2,36,66,37,36,2,37,66,36,17,3,4,66,17,4,196,66,198,31,66,37,36,2,37,5,36,66,37, +36,5,17,65,4,3,31,4,7,65,4,65,0,2,26,136,52,65,37,2,36,66,37,36,2,37,2,36,65,37,198,52, +2,31,5,7,65,4,65,0,2,26,200,102,5,7,65,4,65,0,2,26,198,123,136,133,52,20,4,3,27,4,7,65, +4,198,132,188,4,30,6,38,10,27,65,4,65,0,5,7,4,33,3,30,6,38,8,27,4,28,65,4,65,0,3,7, +13,28,9,27,5,28,65,4,65,0,2,7,4,28,4,30,3,38,5,30,5,27,4,28,3,17,65,4,66,0,7,4, +28,11,1,10,27,4,17,65,4,65,0,4,28,4,1,9,30,6,26,7,1,65,4,65,0,3,28,4,1,8,30,5, +26,4,30,6,7,65,4,65,0,2,28,4,1,6,30,9,26,9,7,65,4,32,32,4,32,32,0,196,129,191,9,1, +3,31,65,4,196,129,191,4,10,4,31,65,4,198,129,192,4,31,3,27,65,4,200,129,189,3,31,4,39,65,4,65, +0,2,10,67,0,17,40,2,41,2,40,2,17,67,41,40,17,134,4,5,17,65,4,7,27,65,4,200,129,182,2,40, +65,17,2,40,6,17,65,4,4,39,3,5,65,4,65,0,2,10,134,47,65,40,2,41,65,40,13,17,65,4,7,5, +65,4,200,129,221,200,57,5,5,2,17,65,4,204,79,202,50,4,5,3,17,65,4,200,130,30,3,5,4,17,65,4, +65,0,2,42,21,4,2,5,5,17,65,4,196,133,161,7,7,6,17,65,4,65,0,30,31,65,4,198,129,173,196,128, +137,4,31,65,4,65,0,2,10,65,0,2,17,2,40,3,17,2,40,3,17,2,40,2,17,2,4,66,17,4,7,31, +65,4,65,0,2,10,67,0,17,40,2,41,67,40,17,40,2,41,134,6,2,41,65,40,4,17,65,4,7,31,65,4, +65,0,2,10,134,30,65,41,2,40,67,17,40,41,2,40,134,6,2,40,65,17,200,62,212,84,3,17,198,35,198,129, +143,65,0,2,26,21,4,7,7,65,4,65,0,6,7,6,27,6,3,5,38,7,7,65,4,65,0,4,7,3,27,7, +1,6,38,10,5,65,4,65,0,4,7,2,27,6,1,6,38,8,5,4,28,65,4,65,0,3,7,4,27,4,3,14, +27,5,28,65,4,65,0,2,7,4,27,4,1,8,27,65,5,11,32,65,4,65,0,5,27,196,129,153,4,27,65,4, +65,0,4,27,13,30,13,26,65,4,65,0,3,27,12,30,6,26,3,30,6,17,65,4,65,0,2,27,10,30,18,26, +65,4,32,32,4,32,32,0,65,0,3,43,23,10,4,28,65,4,65,0,5,43,20,26,5,28,65,4,198,135,171,65, +0,2,17,12,23,2,10,12,0,2,5,65,4,65,0,2,17,65,23,10,24,65,16,198,131,48,2,17,65,4,2,5, +65,4,65,0,2,17,66,23,24,2,23,2,16,2,23,2,16,66,24,16,2,10,66,0,17,8,4,66,17,4,2,5, +65,4,212,33,69,15,44,45,16,46,2,44,67,16,17,4,2,5,65,4,212,51,65,24,2,15,66,47,24,2,15,67, +47,17,4,2,5,65,4,208,20,208,22,208,24,208,26,208,28,208,30,208,32,208,34,208,36,208,38,208,40,212,92,65, +15,2,44,66,16,15,2,44,67,48,17,4,2,5,65,4,212,111,8,0,66,17,4,2,5,65,4,202,128,143,66,10, +7,196,132,123,65,4,2,5,65,4,65,0,2,17,12,16,2,7,12,4,2,5,65,4,65,0,196,136,212,15,7,65, +4,65,0,5,10,25,17,65,4,198,136,120,65,0,3,17,3,28,7,32,9,28,8,17,65,4,65,0,7,28,5,32, +13,28,5,17,65,4,198,13,11,32,7,17,65,4,65,0,5,28,196,46,32,32,4 +}; + +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe -p exo wire_wolf.gif + +const unsigned short data_wire_wolf_gif::pal[115] PROGMEM_FAR = +{ +0,224,12512,12519,12775,25319,25063,25327,25583,39919,39655,25056,39911,160,10464,6272,12768,6240,32,2081,52199,39663,52471,52727,40183,52455,40175,52463,25575,40439,13031,52719, +33,34,65,98,97,10240,12288,51431,63719,38919,66,38912,51207,24576,51200,63495,24807,52983,24800,39143,65271,63983,24583,65527,63727,1,231,64999,487,65263,65007,25312, +39927,13295,13039,64743,25839,40167,50391,25847,64751,65519,65255,2082,39648,51936,52711,64487,52192,63975,39136,40447,25591,52735,25071,26111,13303,40191,52479,12783,2113,2112,64,10432, +21197,16901,192,40703,39399,52991,51687,26367,51695,14079,751,26103,4291,13559,13815,13823,743,2080,2178 +}; + +const unsigned char data_wire_wolf_gif::data[27211] PROGMEM_FAR = +{ +32,34,70,204,140,168,102,103,4,68,70,104,136,138,202,136,4,34,0,70,102,104,172,172,2,34,0,0,0,208,223,208, +1,2,152,65,0,0,1,225,1,236,2,3,4,21,92,142,73,68,0,0,6,7,2,3,4,144,5,106,203,6,78,2, +187,18,1,160,0,8,14,2,4,5,146,137,208,7,178,181,1,136,68,6,161,37,64,0,208,244,2,4,5,7,154,216, +6,252,4,2,1,53,22,6,194,224,0,96,122,2,4,7,8,45,5,87,26,122,1,2,41,3,141,106,182,3,232,0, +30,27,2,3,94,4,8,9,149,2,173,1,68,136,244,4,80,6,162,197,105,93,5,111,128,1,129,4,69,6,125,10, +1,77,9,5,173,2,116,38,171,19,5,7,66,117,4,209,3,112,138,14,40,0,44,58,2,3,4,23,77,98,61,1, +84,150,219,6,5,148,148,176,7,95,6,4,208,3,5,199,192,1,0,1,97,2,41,232,1,4,160,3,73,232,5,7, +77,154,6,199,97,4,112,150,17,43,51,128,2,67,2,6,166,11,36,234,1,211,3,171,12,5,131,7,4,5,97,2, +164,9,228,0,7,130,2,139,6,10,11,195,54,4,178,91,1,7,9,8,5,200,66,5,6,245,190,80,0,58,30,13, +2,11,6,65,85,208,83,209,4,5,7,97,8,69,9,12,102,125,5,6,33,129,43,1,0,0,14,135,14,2,6,8, +40,2,41,246,4,5,7,20,8,88,9,43,5,96,79,88,97,6,192,1,0,3,129,15,193,2,6,3,16,2,41,244, +4,5,7,81,8,9,96,184,194,5,32,248,3,2,4,1,0,0,108,58,17,2,6,36,11,65,133,2,14,191,1,4, +3,99,241,5,8,9,2,218,7,5,19,35,82,2,112,64,3,129,1,162,6,2,68,97,11,136,16,123,2,174,197,94, +1,140,8,9,97,7,68,5,138,138,2,48,3,0,0,224,81,6,2,98,73,11,21,16,6,3,85,4,91,8,9,24, +47,5,69,7,10,48,183,4,64,1,176,162,6,2,196,35,162,16,209,50,125,0,33,183,227,3,8,9,10,5,44,7, +21,210,4,6,111,192,3,65,6,68,2,137,11,85,9,147,23,65,111,1,34,163,4,7,140,9,23,16,74,5,202,4, +128,1,144,245,2,6,11,16,24,233,176,170,11,75,64,42,238,5,8,9,16,72,7,93,3,122,1,20,0,12,7,2, +5,6,169,16,11,81,116,4,59,175,1,32,53,252,6,8,9,97,3,107,7,67,130,0,27,13,16,6,20,2,81,5, +11,99,146,218,58,246,1,11,196,3,221,109,4,7,27,5,9,13,8,0,102,34,43,4,0,6,67,2,6,69,16,141, +5,35,174,209,66,27,0,1,5,232,3,88,183,81,122,5,253,16,4,9,11,57,201,46,193,96,0,184,116,2,6,5, +132,53,16,130,200,104,67,0,1,98,47,4,161,3,149,13,87,233,4,2,10,104,8,7,107,4,110,213,18,134,11,128, +15,195,2,5,69,6,73,16,28,96,212,134,0,1,134,254,4,192,150,147,97,157,183,5,185,7,203,210,6,40,148,216, +57,64,3,208,209,2,10,6,81,18,5,11,132,5,7,94,1,133,168,4,179,3,172,206,216,210,4,6,9,171,130,64, +179,32,76,161,18,80,1,176,104,16,10,208,6,2,156,5,130,180,16,52,81,98,11,236,0,1,40,4,179,3,33,140, +217,19,84,219,104,135,1,251,1,164,2,17,186,19,18,224,3,32,232,1,6,10,208,5,2,178,21,67,40,43,123,11, +91,1,123,3,160,4,169,101,104,93,91,8,134,3,216,76,210,96,176,0,220,61,2,5,10,12,53,81,6,66,172,21, +18,66,0,216,163,3,4,96,214,1,187,2,164,48,41,22,234,49,211,96,184,0,208,59,2,6,10,5,22,198,16,5, +23,6,93,140,26,241,12,194,4,135,21,165,3,116,145,1,4,253,149,1,168,27,56,1,213,22,224,2,224,232,6,5, +10,77,84,17,201,109,60,0,1,9,6,17,188,2,138,142,3,4,6,149,1,190,89,20,3,21,205,26,219,2,13,249, +82,198,36,160,78,215,10,128,117,0,5,65,2,208,6,5,155,5,107,12,146,180,4,16,252,2,0,9,5,75,104,1, +13,182,3,4,135,1,169,37,14,33,73,4,182,132,221,1,3,39,21,207,28,152,164,139,80,1,48,99,2,6,13,5, +10,9,174,27,126,1,12,2,17,76,4,135,2,3,4,69,6,54,139,16,112,74,72,148,16,137,142,10,180,1,208,92, +136,69,16,22,224,164,228,128,8,64,247,48,145,11,232,10,161,5,113,35,4,161,1,122,2,194,111,1,92,128,138,216, +3,4,90,6,55,196,167,21,152,160,213,98,93,3,14,99,20,47,211,44,39,176,160,154,0,30,131,1,6,165,5,16, +164,2,179,43,1,251,47,9,137,4,187,66,201,86,20,3,44,34,236,4,28,178,177,82,89,106,80,104,234,146,165,3, +197,156,32,27,128,143,84,144,236,221,200,1,216,47,2,236,176,16,178,11,70,5,89,237,213,12,5,5,111,1,162,2, +3,192,101,216,4,58,109,24,228,181,201,68,11,177,90,92,86,135,1,250,229,9,80,199,103,121,55,68,0,228,27,2, +16,250,88,164,40,5,62,13,162,143,0,1,12,150,5,235,54,144,55,11,3,17,138,36,164,4,42,6,180,92,64,83, +174,16,145,108,86,151,5,173,1,75,0,167,212,36,153,98,77,8,5,224,0,216,23,1,238,208,5,155,16,145,10,67, +61,2,7,200,71,216,0,163,104,138,3,100,148,44,75,66,226,78,4,66,6,75,80,69,179,23,227,250,161,64,188,57, +36,23,43,7,38,97,230,4,124,181,204,9,5,224,1,160,58,6,10,12,79,212,34,160,150,208,242,31,2,13,181,169, +0,139,193,75,4,5,196,84,3,66,9,49,40,168,6,75,84,146,92,93,137,29,171,96,183,31,239,1,5,3,52,226, +149,17,230,133,198,101,208,9,7,204,0,100,14,2,10,12,132,200,195,6,143,236,144,2,61,199,201,163,252,226,10,231, +216,3,4,51,54,110,3,33,20,150,185,80,91,52,116,130,41,107,16,96,193,151,108,3,1,254,148,235,7,149,2,5, +196,234,70,216,5,4,187,8,7,126,0,94,3,173,209,6,10,12,16,88,20,8,175,10,6,16,2,32,123,180,11,16, +22,245,1,27,2,231,144,4,40,87,2,113,170,3,148,218,20,220,230,133,177,5,93,18,212,70,21,174,209,129,75,5, +7,48,165,157,5,12,198,4,184,204,5,207,8,7,9,114,0,11,1,2,71,11,5,10,6,10,20,44,12,26,191,110, +16,194,11,169,10,2,235,57,140,245,4,210,62,109,22,64,138,168,1,201,3,67,56,204,65,104,38,37,240,178,45,10, +97,28,55,252,5,8,180,85,204,77,129,98,37,84,7,57,78,231,8,9,108,128,20,195,2,16,194,6,10,7,236,80, +11,118,253,10,12,20,36,13,17,2,204,173,253,2,171,251,153,215,67,56,224,116,4,3,162,1,37,9,208,3,179,61, +1,114,107,172,160,140,48,133,147,17,210,24,22,133,179,139,7,183,9,101,145,172,85,157,219,153,45,9,8,121,7,128, +39,0,20,0,239,159,150,66,12,174,11,2,65,86,211,86,1,40,63,18,5,106,139,219,16,244,117,2,1,12,65,120, +54,3,4,32,162,151,66,109,45,1,19,38,23,197,74,148,58,233,74,4,148,9,51,10,212,131,175,5,217,9,8,213, +97,21,98,89,10,219,243,37,9,210,7,8,67,142,0,82,11,2,249,164,10,22,191,16,197,59,11,11,6,81,162,74, +174,12,254,117,4,5,7,53,140,125,11,235,1,115,232,1,3,199,2,4,24,179,52,3,164,134,89,16,117,42,158,29, +212,75,4,1,37,170,36,214,104,90,148,180,5,9,157,122,21,201,47,7,86,10,97,135,122,244,9,22,8,7,151,0, +39,135,2,11,6,127,43,8,16,130,11,69,208,10,12,154,22,33,5,105,20,72,96,84,57,68,52,6,2,63,193,21, +1,180,3,4,198,74,23,148,4,117,16,6,137,166,93,193,104,147,2,201,192,117,18,80,148,130,153,123,32,89,57,155, +8,9,97,125,21,5,102,10,236,6,0,68,54,72,190,23,24,7,8,204,0,75,3,212,62,149,223,252,145,14,11,5, +12,193,69,137,111,248,228,10,12,20,21,4,10,10,40,5,127,106,147,16,3,4,96,160,162,104,3,92,148,98,91,99, +45,131,65,206,33,203,117,27,2,41,5,152,255,109,5,223,8,7,9,76,157,4,175,10,21,98,100,6,1,215,49,211, +169,1,24,161,8,192,0,36,131,250,80,62,123,16,50,111,243,162,5,12,193,93,11,16,38,11,114,10,12,20,5,34, +64,150,57,12,28,135,73,67,6,233,36,134,136,200,1,3,51,20,173,5,180,9,49,48,28,241,167,118,72,27,33,188, +144,44,54,8,9,11,53,76,159,10,166,9,37,153,8,7,60,191,22,53,1,64,8,160,119,130,228,64,201,163,5,10, +12,66,171,208,16,214,11,77,136,20,22,101,169,25,10,42,220,196,198,224,143,2,4,16,0,168,1,231,174,137,3,141, +36,38,123,153,155,131,40,207,1,38,48,113,155,192,234,70,228,177,68,73,242,3,7,9,4,193,219,29,114,29,4,9, +7,14,170,91,5,4,26,8,198,0,124,15,157,5,168,183,83,16,192,122,172,12,20,108,32,35,163,20,6,238,10,141, +6,11,188,77,207,31,69,10,50,246,238,15,99,97,170,168,50,74,144,179,1,25,21,212,65,97,1,110,137,174,129,174, +29,42,164,167,8,184,16,102,108,88,9,90,3,153,134,32,148,46,228,113,1,9,26,96,243,7,2,217,0,112,40,1, +22,169,245,142,232,65,5,69,12,130,175,251,103,25,209,27,5,25,127,10,6,11,16,66,129,134,16,2,127,3,139,23, +120,159,22,61,53,5,216,20,204,186,234,48,44,218,19,92,99,250,51,10,145,174,32,94,24,220,9,26,5,176,88,130, +230,254,204,98,211,174,23,42,11,7,130,0,200,65,2,64,234,19,144,6,236,5,10,12,21,67,41,25,175,9,27,26, +40,171,25,84,131,116,48,186,6,97,5,1,222,200,4,30,82,173,118,33,156,174,5,205,1,37,42,218,52,37,196,164, +197,113,133,104,37,183,164,161,198,150,30,8,26,5,9,154,4,193,118,6,14,6,73,120,6,116,235,24,56,201,162,8, +216,94,18,132,192,16,3,240,14,15,246,190,4,227,14,5,10,12,192,140,24,105,6,16,20,244,26,27,53,18,238,5, +16,1,56,119,80,97,5,96,206,97,201,38,38,0,210,5,187,60,1,34,141,9,119,116,10,134,209,253,28,10,174,41, +90,32,226,4,8,26,8,5,141,161,9,211,41,178,145,92,182,150,24,214,7,5,147,109,122,18,19,0,64,14,230,16, +127,107,3,164,208,236,52,4,16,223,199,10,68,12,65,9,25,23,6,41,50,118,26,27,132,215,182,5,67,175,187,1, +82,14,152,64,213,226,20,93,198,67,20,208,18,145,1,37,50,205,4,97,77,209,104,88,152,70,164,13,148,173,33,4, +198,7,26,2,57,232,50,3,157,8,116,38,210,23,181,4,178,100,58,8,7,5,109,87,161,18,48,15,128,115,247,254, +140,31,41,128,184,233,10,234,16,5,34,12,66,9,173,5,149,50,221,118,103,235,33,72,6,250,143,217,129,131,5,131, +187,48,204,11,179,2,188,51,10,153,16,164,44,132,155,178,39,160,197,90,3,176,85,120,0,83,90,5,9,82,26,11, +147,89,202,157,4,8,22,9,38,26,23,194,5,232,20,104,9,219,7,5,166,122,18,96,30,193,254,79,210,65,235,35, +157,106,15,91,252,80,156,10,133,12,13,233,218,229,2,1,166,12,9,146,193,115,237,1,4,161,133,28,129,50,4,227, +141,104,51,35,52,105,4,98,164,161,82,42,34,217,115,6,16,103,49,246,3,164,22,142,0,123,4,7,73,232,118,26, +2,57,121,4,74,8,195,7,9,200,24,23,43,6,41,81,9,26,233,8,5,7,180,164,18,1,228,47,1,226,58,200, +5,128,124,37,185,203,68,167,10,12,77,142,162,50,16,1,67,81,47,63,142,179,106,9,32,187,28,60,4,16,37,97, +48,142,1,115,4,5,24,6,98,133,112,233,16,75,93,221,205,16,49,99,3,106,32,113,5,0,241,238,150,8,26,133, +196,113,126,79,8,9,73,183,29,140,23,51,8,4,72,220,238,24,7,82,25,233,18,128,119,8,2,181,199,174,10,134, +59,248,197,168,31,28,16,240,220,5,26,12,23,145,1,229,5,112,246,194,122,144,9,112,198,160,106,216,88,2,193,160, +125,180,5,28,140,192,158,201,74,26,247,102,237,199,69,3,104,105,21,44,90,12,3,127,163,26,9,24,66,160,145,207, +63,8,109,216,23,17,136,10,240,155,142,147,225,83,8,104,1,220,47,2,207,78,6,122,44,10,22,126,184,147,24,29, +251,109,26,10,12,237,42,5,166,27,201,236,125,249,26,229,94,1,18,133,208,39,197,129,80,31,190,6,28,135,144,84, +162,209,153,65,148,238,94,138,106,21,102,108,54,71,48,48,31,239,5,9,24,16,66,8,253,222,3,9,130,219,136,23, +6,64,237,74,22,186,30,7,109,0,59,0,189,231,6,127,12,69,10,85,197,187,99,13,65,82,70,93,171,49,88,129, +126,176,108,108,8,26,31,150,52,140,66,140,122,3,2,106,83,98,48,21,3,159,28,49,99,128,103,17,79,31,4,98, +26,167,85,186,104,214,1,92,134,3,151,117,250,24,23,29,108,9,227,12,8,23,48,43,4,109,7,216,21,126,24,26, +5,30,170,108,224,28,128,127,135,70,254,141,10,12,140,30,112,253,163,196,168,193,198,112,148,27,177,113,200,8,9,88, +26,46,52,133,14,177,137,92,57,100,132,5,88,142,116,194,155,113,153,228,6,162,234,184,157,205,164,152,214,1,84,4, +2,169,112,221,134,29,24,131,74,24,96,9,23,128,223,147,111,26,99,118,40,91,24,8,186,30,6,180,3,136,11,160, +253,196,5,25,6,1,72,129,230,216,10,5,210,6,21,16,95,80,110,174,84,9,65,8,90,26,192,245,185,67,11,3, +186,78,1,54,192,169,32,92,79,37,9,121,37,9,117,16,108,8,227,142,120,228,207,144,173,68,210,196,150,35,69,52, +187,209,41,26,234,29,24,55,161,129,173,195,9,23,10,24,9,145,8,69,40,229,60,164,7,206,0,111,3,167,62,39, +83,5,96,163,246,31,242,129,126,139,63,177,10,227,6,6,167,215,16,5,236,8,9,26,20,16,226,206,4,232,23,194, +227,108,52,3,3,26,83,188,28,144,153,57,166,160,110,17,6,189,14,18,6,216,115,18,92,102,10,57,123,9,23,29, +23,174,24,57,144,92,99,5,24,140,23,23,37,61,84,84,141,213,8,227,211,118,64,27,64,125,224,154,147,198,34,72, +105,3,2,62,61,16,242,91,67,219,15,243,9,26,69,31,54,57,8,5,186,39,16,242,5,131,8,38,202,33,182,80, +32,53,1,250,176,106,56,150,129,73,16,55,1,20,20,48,203,68,1,170,246,5,22,26,23,10,15,16,115,2,9,32, +67,30,29,111,200,73,24,23,8,100,94,20,233,9,7,4,25,208,13,129,2,16,98,3,4,2,121,211,16,40,88,3, +22,15,144,219,92,45,82,54,8,26,21,27,6,1,60,66,5,8,4,189,91,109,110,9,74,108,10,64,181,58,5,28, +65,82,23,146,176,150,106,70,83,3,100,53,3,45,11,146,242,26,23,18,24,208,157,7,4,253,210,32,33,107,7,44, +11,18,46,5,2,60,44,206,235,8,3,128,107,8,2,130,3,194,4,123,133,183,127,26,98,6,220,3,228,107,164,22, +81,1,109,190,94,9,26,8,5,84,16,168,162,9,8,161,125,28,153,67,26,27,67,148,176,190,10,3,225,102,221,28, +5,1,164,83,20,182,16,6,21,152,86,226,107,66,74,2,113,200,26,23,69,8,250,246,72,7,47,239,32,34,35,187, +36,124,9,23,32,121,233,2,140,53,251,6,8,150,1,80,40,37,18,126,38,39,40,41,12,220,248,140,14,157,115,181, +59,241,3,242,197,171,18,133,211,35,133,239,8,162,9,26,196,32,188,16,169,81,210,18,72,175,8,4,10,25,179,31, +32,169,100,155,96,134,28,6,212,195,46,228,32,113,196,177,248,26,113,90,30,160,184,129,123,0,43,116,149,2,185,93, +236,118,23,17,22,126,244,3,9,7,65,1,127,122,32,34,35,46,42,124,26,179,45,239,5,160,19,7,184,25,224,10, +33,43,202,44,41,123,38,39,10,40,33,45,183,62,32,128,114,134,186,6,5,40,10,99,46,64,123,233,5,175,23,233, +93,28,216,8,26,72,9,163,205,220,107,5,89,27,78,8,81,45,138,22,31,10,174,88,21,164,86,28,12,6,2,176, +221,28,5,49,72,18,225,6,77,16,21,218,3,184,192,17,55,144,52,20,23,14,223,9,209,24,7,1,6,253,236,18, +32,34,12,150,9,23,65,91,4,229,21,41,156,106,7,168,5,0,244,38,46,44,47,109,39,45,106,48,176,162,40,44, +208,231,140,36,2,34,3,28,123,42,5,52,10,12,144,200,254,68,74,16,129,172,115,143,13,6,194,228,8,9,26,21, +22,188,176,60,157,27,31,23,37,87,8,247,16,160,165,16,179,4,102,97,49,65,30,27,226,75,4,26,28,29,243,180, +73,142,105,7,57,2,141,6,51,72,48,107,2,21,11,143,24,109,208,23,171,82,114,221,23,9,7,189,7,9,18,1, +93,46,9,82,30,89,5,49,52,189,5,252,2,120,116,46,44,47,88,40,126,39,48,38,0,50,51,194,44,202,115,183, +227,16,145,3,11,115,199,5,208,10,12,154,15,6,6,16,45,82,202,141,105,224,107,5,209,28,9,26,66,147,4,240, +125,92,78,28,27,23,65,88,8,188,157,193,99,31,78,2,77,91,49,23,74,52,32,90,31,142,27,12,5,239,116,6, +28,39,4,88,193,160,210,133,60,212,73,146,130,1,171,107,9,205,23,22,4,3,104,123,7,23,24,8,150,7,131,161, +23,112,212,55,240,208,92,7,112,128,18,132,223,45,46,176,47,176,40,176,53,250,39,54,0,38,41,74,62,228,2,97, +3,65,200,132,129,220,242,9,12,10,11,141,53,87,9,105,108,47,77,28,9,14,1,73,161,233,60,178,49,34,23,41, +238,4,6,221,28,26,31,12,172,91,52,49,73,22,55,10,144,221,23,26,23,96,145,134,102,65,116,71,8,5,6,31, +121,156,96,25,96,134,180,216,7,22,60,132,8,158,52,183,22,26,120,2,16,94,8,170,99,64,43,7,43,44,47,97, +40,97,56,161,53,54,93,44,38,254,40,140,2,35,3,200,10,26,6,5,36,10,91,9,222,10,37,92,6,107,75,102, +44,4,178,66,162,75,6,28,104,126,180,8,52,108,23,49,88,9,188,156,63,28,9,26,27,45,22,219,31,52,200,55, +129,110,31,28,16,63,103,75,5,12,166,9,121,236,1,110,8,5,6,168,212,161,118,17,1,85,188,192,128,152,84,116, +26,23,24,152,19,155,213,98,57,165,1,23,247,4,22,223,9,7,80,2,160,121,38,43,44,47,8,40,194,39,137,41, +205,95,97,22,2,12,102,164,230,108,4,5,32,139,4,76,147,125,6,18,225,54,44,67,232,67,153,28,167,9,2,117, +215,210,23,52,34,198,26,164,170,161,26,119,81,93,31,52,17,55,4,26,31,107,16,184,5,175,38,58,40,8,138,29, +25,158,31,193,44,128,169,156,86,183,24,147,21,203,5,7,113,32,57,37,126,233,8,9,46,3,96,68,1,51,11,3, +5,129,0,170,3,131,38,43,69,44,20,47,88,40,72,39,209,43,65,233,2,3,49,80,56,225,234,162,5,5,202,42, +21,235,166,116,97,184,161,197,52,37,2,227,9,19,130,122,26,52,44,23,69,138,182,236,170,69,26,119,97,31,110,145, +55,33,9,11,205,188,221,2,16,50,218,26,141,28,6,6,217,5,187,8,183,151,10,89,1,152,185,3,128,38,93,26, +233,24,23,22,180,7,83,8,101,128,57,149,251,3,55,144,25,64,245,105,6,5,42,2,40,108,58,1,1,235,122,38, +45,43,44,44,54,47,40,24,39,45,163,95,150,194,3,45,65,217,142,80,206,174,81,18,198,11,188,208,151,147,200,46, +53,174,109,5,18,28,226,213,122,52,41,49,206,23,118,77,42,168,9,117,249,4,31,52,10,55,22,114,8,105,11,59, +93,2,73,26,172,36,34,9,230,188,92,16,81,67,196,13,33,183,42,59,189,3,161,80,243,59,26,24,139,23,64,142, +232,24,9,8,175,3,57,72,2,16,77,1,188,8,8,30,16,0,172,120,60,58,1,32,5,95,38,232,45,41,44,166, +47,187,80,179,232,3,38,120,24,3,215,175,10,131,6,185,146,207,94,6,5,193,73,1,230,216,26,52,85,49,45,155, +210,166,9,134,153,16,27,82,138,55,4,175,31,61,187,11,239,62,63,2,52,72,207,253,5,12,27,31,9,205,125,39, +75,28,6,80,211,10,16,205,32,210,142,177,186,44,182,9,24,29,26,23,22,171,176,175,24,64,215,9,7,164,57,3, +232,7,204,22,149,16,7,130,8,4,133,65,13,108,66,40,30,30,61,1,32,114,147,144,8,57,38,45,43,41,180,79, +80,121,209,158,64,232,6,169,229,151,111,72,31,9,9,68,81,10,5,116,45,197,111,93,180,205,52,97,23,141,31,97, +83,28,174,9,206,164,31,170,168,55,90,106,12,5,108,31,61,111,67,62,11,16,90,2,81,69,31,27,26,28,120,240, +14,141,6,14,136,84,98,208,42,129,106,138,70,127,26,24,177,22,50,23,7,125,26,149,19,9,64,3,216,78,2,221, +138,131,160,129,7,97,8,7,9,68,21,3,77,206,116,204,2,36,3,4,72,114,141,235,235,147,66,209,190,232,193,186, +40,60,217,249,117,134,206,10,128,239,246,6,97,244,187,99,13,1,49,154,26,52,31,188,39,212,9,84,9,152,189,31, +13,229,55,11,180,209,25,61,69,90,27,171,203,106,62,59,110,26,5,93,9,14,31,27,26,4,118,206,157,28,223,25, +130,40,231,78,38,187,1,254,250,4,9,26,24,19,226,23,22,2,58,70,243,123,24,9,162,3,192,30,225,132,7,28, +32,8,225,68,12,26,131,131,171,5,190,6,50,106,178,16,63,103,55,61,141,234,178,2,97,208,12,44,3,162,202,87, +185,6,15,226,54,101,219,147,188,94,12,40,55,5,116,130,232,55,213,143,203,45,52,25,49,23,45,204,149,16,53,28, +10,159,27,31,62,52,160,55,187,235,25,61,176,232,27,1,62,118,202,111,20,9,25,4,171,26,31,164,49,171,168,233, +151,131,24,59,160,231,94,138,192,61,181,26,30,7,4,24,22,144,208,118,125,8,24,26,220,128,240,23,2,219,32,125, +3,8,8,20,20,68,65,71,129,178,8,209,214,118,207,254,66,104,36,48,53,239,12,196,111,22,127,184,168,107,3,132, +2,208,185,133,139,252,105,16,172,40,2,214,16,200,45,4,10,187,32,200,55,234,148,153,244,26,52,119,23,9,0,57, +189,110,200,19,52,9,31,55,155,55,27,69,235,61,16,105,2,253,61,69,0,72,59,63,71,142,162,26,49,140,31,5, +194,41,28,117,93,244,1,210,145,205,188,225,106,49,202,14,39,9,26,24,109,39,22,92,159,19,66,160,63,132,1,121, +227,253,129,2,8,5,5,68,236,0,161,71,38,198,68,216,79,130,223,80,6,255,100,11,37,127,250,214,6,51,240,72, +62,250,68,8,227,94,119,101,147,157,1,36,170,235,194,3,185,50,88,5,199,161,199,28,121,142,210,23,49,69,1,154, +16,221,138,153,187,100,176,31,213,52,55,117,250,25,61,73,227,14,69,16,61,151,5,59,11,175,0,27,162,26,52,133, +95,104,188,94,9,198,110,1,93,135,117,144,4,17,235,223,9,101,37,40,22,108,128,70,8,1,247,144,7,194,8,5, +131,226,189,136,20,9,239,110,96,230,58,209,5,132,4,121,156,3,244,161,69,132,106,143,184,213,3,28,160,45,25,116, +36,155,2,140,130,52,78,172,87,92,181,23,51,81,242,216,23,21,6,86,6,135,73,65,40,249,27,31,52,55,23,159, +215,62,61,8,52,61,12,123,62,1,67,11,155,27,123,165,52,6,118,28,162,87,9,156,50,167,3,29,228,129,160,64, +238,241,9,102,181,22,11,7,72,2,40,10,217,5,129,7,193,8,16,94,9,198,64,224,139,136,35,9,7,5,214,144, +5,197,4,3,236,172,6,103,8,213,31,104,231,108,14,168,121,235,53,6,230,134,7,130,9,144,138,42,203,18,105,236, +12,61,2,4,27,31,129,85,2,104,236,18,61,12,190,26,27,49,52,135,75,16,12,227,31,61,55,14,62,12,61,196, +238,69,2,10,119,27,26,31,164,52,237,77,51,223,29,153,191,61,153,1,75,8,31,152,43,195,135,26,171,7,24,172, +22,77,124,165,64,34,129,182,28,186,7,5,132,8,8,7,196,123,89,5,196,5,50,221,246,113,4,2,125,187,198,130, +56,155,1,30,114,252,149,198,121,162,104,153,104,58,121,6,106,93,155,196,56,2,230,191,12,212,92,57,6,16,132,188, +5,221,31,178,187,128,197,32,76,219,16,5,154,52,15,135,74,25,12,194,61,55,14,12,62,61,196,220,2,20,247,178, +203,31,19,76,240,114,28,226,109,200,135,1,166,24,10,242,114,231,190,226,24,22,185,156,96,2,40,30,50,7,126,5, +132,8,20,32,9,224,219,116,68,245,6,30,92,97,152,141,69,142,63,224,129,211,46,51,215,223,4,6,184,254,113,82, +46,32,40,137,36,137,140,11,117,222,82,12,212,242,166,121,104,26,51,20,1,38,32,42,62,16,12,26,31,52,145,239, +233,61,62,5,194,55,11,61,70,25,63,20,143,211,26,9,55,90,28,25,148,92,81,166,224,185,0,240,2,107,214,206, +92,247,234,24,14,63,19,76,2,72,27,83,7,244,11,136,138,24,53,33,8,7,8,9,64,212,187,48,127,73,253,86, +6,135,214,73,246,131,205,16,213,1,158,187,126,57,169,6,168,115,130,155,94,0,196,54,82,19,1,224,98,8,51,55, +90,9,26,101,43,1,66,6,117,153,185,197,23,49,91,25,210,61,16,134,55,29,12,62,61,28,10,20,67,172,53,175, +153,28,162,26,9,181,2,163,237,36,130,154,1,255,76,250,47,24,187,22,89,174,75,32,160,4,176,125,249,3,222,158, +102,57,227,12,32,8,192,12,136,108,207,1,207,203,235,44,3,92,79,247,16,88,3,21,194,250,112,216,196,13,17,210, +194,8,162,184,6,46,170,4,105,132,28,24,239,5,186,172,182,1,113,0,162,219,156,26,31,69,181,210,61,63,198,73, +55,11,244,62,12,61,81,10,67,238,0,31,26,92,5,193,46,132,204,160,160,228,3,149,30,1,0,159,187,4,7,200, +233,24,8,87,22,233,0,19,57,128,77,11,3,216,7,202,24,15,158,8,4,132,7,171,24,9,189,6,243,136,44,39, +248,4,191,95,238,32,176,3,92,158,214,4,222,191,89,208,196,10,9,5,240,40,131,220,130,196,148,95,213,60,225,68, +21,141,95,242,187,3,28,208,165,9,217,26,28,182,76,77,198,78,61,140,73,55,96,190,61,12,216,253,11,10,67,0, +4,16,212,118,58,12,26,28,199,166,24,51,8,100,137,10,32,127,165,85,9,224,19,239,8,24,22,70,164,32,1,60, +35,4,9,5,7,240,132,7,178,8,10,13,72,195,84,65,154,4,47,39,90,6,76,143,2,240,64,125,132,123,83,44, +250,207,192,24,57,142,105,48,209,166,33,156,49,239,197,103,120,54,32,64,28,24,81,204,229,248,175,200,11,67,212,61, +99,73,55,5,246,61,69,199,2,20,79,213,0,31,49,122,28,26,233,60,208,158,136,42,56,180,59,192,230,12,109,8, +237,148,22,39,0,0,164,17,4,1,215,158,0,80,136,66,48,65,8,128,73,137,174,6,4,196,23,218,132,196,221,176, +94,164,2,246,67,51,120,100,79,225,7,85,40,12,203,208,4,1,58,149,66,181,53,45,154,198,1,129,36,253,11,228, +73,24,200,28,213,108,80,76,190,78,61,73,55,48,95,61,25,75,78,0,229,67,11,1,147,147,12,163,31,26,28,185, +2,135,8,96,176,238,38,48,47,213,230,7,216,241,91,22,24,80,10,96,251,39,197,253,35,223,12,33,8,132,66,129, +60,37,121,5,6,8,4,187,29,33,114,46,35,98,2,8,3,243,151,132,103,138,5,46,9,193,212,127,29,26,11,144, +185,8,78,192,128,247,109,40,28,177,12,28,151,120,10,6,53,138,17,152,4,16,107,100,67,250,61,46,12,225,63,31, +52,73,61,164,62,230,55,91,4,9,174,220,209,31,27,121,209,60,189,169,129,190,146,31,121,112,230,5,120,115,44,22, +96,10,129,1,55,182,44,5,32,31,208,48,105,7,30,4,14,61,6,12,97,112,180,178,4,38,208,126,136,62,232,92, +201,44,221,31,145,69,220,9,135,249,88,26,68,9,18,7,216,40,83,219,6,161,12,189,177,28,112,26,6,123,219,255, +131,104,18,43,89,28,242,11,20,136,168,201,58,25,61,43,16,219,20,83,9,115,121,125,49,31,229,219,102,97,15,168, +220,187,182,67,157,7,250,229,162,64,159,8,24,22,181,7,0,168,15,245,65,23,6,5,12,15,216,16,13,178,160,142, +64,231,104,81,119,68,60,176,249,48,160,3,92,44,149,4,18,114,4,236,101,225,9,128,162,174,2,0,91,135,107,2, +114,168,26,44,157,138,86,16,253,0,251,242,209,234,142,225,228,13,19,136,174,11,10,89,116,27,62,52,40,233,219,25, +118,10,76,26,5,190,169,12,243,31,27,9,28,118,124,196,11,1,117,217,9,64,154,71,154,212,24,8,169,22,5,106, +1,84,7,126,67,236,162,7,24,64,8,128,18,110,25,8,6,186,137,41,194,4,243,144,2,176,3,17,149,1,222,17, +87,232,58,37,164,9,96,221,199,7,17,4,19,201,235,127,180,2,80,58,9,26,76,133,224,251,192,1,140,1,234,84, +1,80,26,163,59,208,85,45,235,10,238,27,12,205,20,62,19,238,76,230,9,214,12,92,214,187,31,229,71,26,131,109, +55,230,83,44,9,34,110,204,109,22,24,201,0,174,3,191,161,87,202,132,7,16,8,64,180,9,63,176,5,130,3,39, +133,210,106,23,140,2,131,181,89,172,1,129,138,44,11,192,159,14,133,115,202,102,195,121,117,27,199,71,50,81,73,212, +37,93,16,66,246,15,191,117,12,35,205,243,28,31,26,72,224,68,9,194,62,190,61,55,149,5,39,129,182,10,187,28, +55,52,179,150,4,58,26,9,230,219,2,160,6,229,235,148,135,155,5,250,246,39,171,20,178,22,24,144,5,96,138,2, +254,68,62,194,8,8,130,26,130,75,219,90,148,220,2,7,41,68,3,231,196,81,193,16,161,3,97,28,87,150,228,3, +120,20,3,144,79,80,19,102,40,175,38,82,81,51,100,61,206,164,28,206,14,24,32,155,4,141,5,147,26,31,166,23, +234,33,195,67,185,71,27,1,73,163,55,69,62,154,6,203,49,218,233,55,229,2,16,218,12,144,242,189,6,5,39,48, +101,156,217,96,244,55,6,204,40,80,24,212,2,192,72,4,72,3,145,6,250,72,8,98,9,26,5,152,53,42,36,5, +5,144,51,49,227,6,238,144,2,160,3,170,59,130,131,251,2,3,28,232,36,100,143,226,96,149,119,2,44,137,40,97, +43,19,227,167,28,67,172,57,16,252,133,113,127,178,110,199,31,49,155,26,18,119,8,45,122,12,218,61,73,53,69,62, +186,16,31,26,85,55,10,210,168,55,211,145,166,250,34,185,146,80,9,39,5,144,9,176,24,106,1,96,51,1,6,129, +12,5,244,116,207,201,136,26,22,67,72,7,193,243,15,42,108,151,3,70,1,47,219,2,10,3,21,28,83,6,220,5, +48,47,109,135,237,146,96,4,156,194,225,114,25,40,40,198,39,45,151,5,12,28,76,83,112,3,220,110,5,163,177,122, +138,63,2,12,26,49,52,31,41,175,4,183,9,171,233,11,12,107,27,20,81,31,55,111,175,16,130,236,212,89,216,10, +210,234,190,33,203,5,80,73,123,112,118,94,7,9,64,149,24,133,8,168,5,144,189,4,177,10,8,23,23,212,8,97, +9,96,34,202,68,39,5,32,46,132,40,2,23,233,9,3,44,233,208,187,136,224,131,3,163,3,93,215,84,18,157,3, +33,185,95,38,45,73,66,254,233,208,12,235,11,212,177,9,149,6,119,131,54,198,235,26,23,210,49,52,151,16,35,250, +231,127,2,11,62,6,16,55,9,151,27,187,28,182,54,84,10,105,119,194,96,249,145,3,72,11,49,29,156,159,7,233, +9,24,64,48,114,192,45,133,4,237,168,80,64,187,253,145,8,65,211,80,26,162,228,27,71,39,57,73,228,44,201,113, +2,126,170,194,3,161,41,13,109,3,232,40,22,227,3,181,11,90,46,174,44,43,38,168,252,215,48,231,218,12,132,199, +232,42,18,162,7,230,57,232,88,220,22,210,26,31,247,28,52,55,9,149,65,178,26,214,118,224,254,178,56,109,99,4, +146,68,16,44,50,2,102,91,163,152,58,164,14,8,250,84,7,172,24,27,116,18,128,44,129,241,98,42,65,158,7,194, +8,48,146,9,91,47,160,69,44,95,40,140,185,228,84,250,176,160,3,94,136,84,169,5,7,54,3,50,67,175,115,197, +180,235,43,44,45,79,38,165,2,86,99,36,207,12,68,2,118,228,50,144,102,37,197,4,114,162,9,26,130,215,52,55, +73,82,216,86,193,135,16,26,31,47,67,136,148,6,201,36,179,130,36,131,151,134,230,90,105,9,7,108,5,24,26,128, +92,11,6,242,142,14,32,88,232,193,0,235,223,84,142,249,88,1,0,181,1,118,85,1,68,3,69,42,200,250,73,193, +5,65,169,218,151,0,245,99,23,38,44,161,45,106,43,198,144,94,167,175,10,12,76,101,32,146,83,92,211,197,7,101, +216,8,9,108,26,4,127,23,49,52,55,203,26,1,7,182,154,205,5,9,43,26,21,215,40,168,192,173,21,129,242,156, +55,33,189,37,32,59,83,185,244,7,3,5,24,42,212,2,224,28,90,5,196,90,3,65,83,180,58,171,8,1,236,9, +206,2,14,52,200,115,24,2,191,80,231,156,173,7,116,198,131,5,6,3,2,92,129,103,215,185,3,69,9,38,79,47, +43,92,183,48,230,166,12,74,66,229,24,1,188,157,131,97,113,90,169,5,196,9,31,4,27,23,26,2,74,52,136,9, +172,175,58,244,242,236,234,199,16,18,192,171,56,17,193,22,184,2,209,16,20,21,180,227,125,9,24,8,38,13,176,11, +192,246,225,9,4,16,46,252,163,170,96,73,132,5,23,4,243,55,225,150,65,142,61,181,3,187,2,129,251,165,168,195, +3,5,114,23,50,226,219,250,45,47,44,38,238,147,134,149,2,232,154,10,12,166,36,208,143,197,87,37,176,226,105,8, +9,44,152,194,26,192,177,232,23,49,52,111,208,95,239,4,38,236,40,16,38,51,176,42,66,193,17,3,121,251,16,67, +150,149,37,217,14,255,7,64,176,24,55,208,11,128,121,24,147,6,132,144,61,236,36,8,23,16,15,39,15,48,104,8, +249,2,57,19,174,10,22,2,192,59,75,1,104,68,42,101,189,2,38,43,41,33,214,49,207,218,242,12,193,213,136,10, +147,200,228,164,218,85,254,204,42,7,6,19,26,131,104,27,31,212,23,52,10,113,233,40,77,2,137,54,201,1,168,205, +12,10,153,129,80,187,118,74,16,57,125,7,4,3,43,24,6,160,23,193,237,15,164,130,56,128,248,6,5,73,201,197, +152,62,248,154,57,236,85,148,158,10,229,221,112,80,145,2,10,240,192,236,92,106,38,19,42,158,38,1,206,84,63,252, +239,77,54,10,12,126,221,101,145,16,27,184,171,216,155,8,9,2,48,41,20,26,58,74,117,31,23,49,220,209,132,2, +138,104,146,69,1,170,217,12,10,180,48,26,134,1,220,180,1,8,186,9,3,24,45,64,64,47,132,1,225,199,27,84, +96,255,16,80,18,151,93,195,82,3,88,156,253,10,62,249,21,248,174,168,40,80,1,228,55,225,203,57,182,3,198,38, +45,81,50,167,144,18,188,81,87,12,135,11,236,106,220,103,44,217,226,9,10,26,9,50,8,2,38,2,211,27,31,32, +174,9,5,108,228,14,32,64,201,46,98,167,6,34,104,111,169,180,14,229,9,167,4,7,161,24,54,160,23,192,242,156, +60,243,136,48,6,7,128,229,170,55,247,3,93,241,213,113,163,2,200,207,208,137,47,196,36,3,129,193,3,74,83,1, +61,90,45,38,149,43,23,49,65,238,8,167,167,10,12,63,72,100,170,5,55,154,7,77,149,17,8,162,9,26,130,42, +25,161,8,221,28,49,37,27,96,87,230,7,0,9,238,34,181,146,215,215,12,10,138,6,218,103,25,218,7,115,196,26, +24,13,160,12,64,113,1,99,234,136,23,132,7,12,197,100,1,228,164,112,195,1,4,8,145,106,111,3,251,72,69,44, +83,13,90,109,140,4,3,163,45,164,44,176,80,77,210,11,97,59,235,12,16,159,5,153,202,182,143,56,99,20,70,8, +9,138,26,8,168,102,135,8,115,33,6,120,98,178,28,39,144,162,136,14,46,10,114,87,112,76,46,77,7,250,94,98, +24,3,68,1,132,39,4,16,22,64,200,50,129,52,77,251,249,96,3,216,147,72,6,69,58,238,165,3,65,16,160,44, +129,217,146,29,179,94,38,43,45,8,255,2,96,213,142,235,108,9,12,212,16,42,137,5,246,60,118,3,233,115,100,209, +8,9,26,65,21,12,195,184,10,230,243,114,1,33,168,10,236,113,81,1,140,203,6,12,54,219,191,51,164,192,239,25, +7,24,10,29,173,64,49,133,6,128,242,3,155,53,37,157,1,6,236,54,246,1,22,2,7,204,18,188,181,151,1,242, +144,77,174,6,163,98,38,132,221,205,4,148,53,99,129,48,11,186,52,9,89,6,107,16,230,12,10,124,94,6,41,210, +97,8,112,17,26,98,48,102,40,21,35,28,195,53,19,160,221,206,24,69,50,139,73,12,148,4,13,161,48,54,255,8, +26,4,30,29,69,24,168,6,48,124,4,28,132,64,200,10,15,141,2,63,163,13,2,3,130,141,165,195,215,32,111,68, +240,227,55,5,2,65,124,43,44,243,134,172,40,3,2,223,180,216,6,9,23,52,80,12,93,146,141,16,179,179,186,7, +105,203,8,11,26,6,77,8,7,128,87,114,83,209,129,106,90,133,136,136,199,124,114,52,51,160,110,225,217,140,71,4, +29,24,85,168,6,64,57,83,20,129,198,201,33,99,34,244,16,99,58,3,9,2,244,143,6,209,142,38,237,182,3,68, +242,187,213,138,24,38,45,106,29,54,229,3,132,23,215,7,9,26,185,5,207,63,12,66,229,18,212,78,15,71,77,36, +56,65,133,7,128,37,212,88,60,3,59,41,139,18,110,160,140,189,10,240,210,18,217,132,172,180,45,168,26,232,4,8, +29,108,128,100,3,240,113,65,231,86,6,32,200,139,4,124,194,3,225,16,88,133,2,254,135,64,47,55,72,183,4,136, +48,227,37,174,44,43,206,27,96,88,54,219,192,223,9,188,238,254,16,6,12,9,5,184,154,134,230,217,209,4,7,8, +132,9,13,24,14,247,1,49,74,187,98,23,2,44,85,101,97,80,81,21,200,179,17,98,86,39,34,218,24,66,29,134, +200,6,96,57,113,201,6,11,161,243,24,135,126,154,43,58,132,18,3,22,2,234,10,129,249,29,18,96,181,238,107,88, +38,163,137,5,2,208,77,68,7,220,167,3,232,7,9,26,104,243,148,12,38,203,2,164,16,101,134,185,206,176,7,198, +8,9,2,53,66,15,15,1,49,33,82,137,216,120,136,212,10,231,6,38,8,177,43,92,44,210,24,194,29,193,210,0, +202,7,222,6,131,224,65,160,178,3,188,77,21,58,195,8,3,93,155,5,9,32,244,81,3,32,17,215,6,74,38,196, +98,135,218,20,145,225,22,224,252,250,3,4,9,26,46,5,130,124,28,9,12,250,69,16,52,20,219,165,7,67,156,164, +8,130,174,212,138,65,217,24,128,169,216,25,215,158,28,210,9,178,16,89,1,95,10,180,46,22,105,24,97,29,96,212, +3,56,62,148,5,228,164,22,34,160,248,166,135,250,132,16,3,90,248,10,246,83,6,154,12,187,1,241,134,243,38,198, +47,43,155,113,5,8,10,177,71,159,175,3,5,9,26,65,224,95,12,9,185,28,12,172,30,146,240,33,52,109,46,245, +40,7,128,144,88,22,70,242,57,113,77,26,243,136,36,133,153,68,139,65,225,37,82,10,197,198,235,5,187,24,29,29, +83,86,160,25,192,120,112,126,175,219,23,181,3,112,128,233,6,4,168,16,141,19,65,148,129,43,118,171,112,45,43,38, +155,244,4,5,7,80,8,164,6,40,5,159,248,126,118,8,26,5,134,12,9,135,69,200,90,12,178,53,2,16,195,106, +241,88,3,7,96,11,121,14,208,48,114,94,121,39,53,148,71,90,6,48,137,3,80,150,46,58,253,7,24,220,106,29, +83,181,0,206,23,1,195,125,112,243,192,177,69,194,0,245,15,231,66,193,108,4,208,105,53,218,43,45,191,104,23,151, +5,7,208,8,9,164,2,197,32,67,23,255,3,4,9,26,168,24,225,238,5,12,11,128,181,28,107,12,207,59,37,130, +144,64,160,16,153,24,142,130,230,246,49,222,90,2,208,80,6,84,178,136,226,99,5,132,168,75,2,200,119,8,24,211, +237,23,83,84,64,52,3,226,156,221,50,19,4,222,126,159,232,32,38,147,207,21,3,130,225,236,2,82,254,85,246,45, +38,41,135,9,59,3,189,209,7,9,8,109,63,165,215,28,3,86,198,5,26,137,24,170,184,28,73,178,89,5,30,120, +160,187,196,10,152,210,55,118,6,170,14,232,186,68,114,208,193,144,27,101,222,232,134,237,103,138,18,178,105,57,153,188, +43,16,20,118,9,29,195,85,83,181,8,0,208,15,138,251,225,232,71,179,127,193,6,163,57,246,196,11,185,128,143,87, +3,228,36,130,126,43,41,147,32,250,204,111,7,8,9,89,213,127,75,188,28,4,4,158,183,7,26,24,159,9,53,24, +87,5,74,28,199,48,110,59,66,64,84,238,178,75,5,118,106,188,6,42,227,22,107,205,17,163,110,220,166,216,171,201, +164,225,89,197,148,14,228,142,9,23,29,149,85,22,7,160,26,2,1,248,163,208,31,113,236,79,184,46,152,96,124,142, +92,61,16,181,14,171,39,45,44,43,133,38,211,4,58,202,7,109,76,99,3,224,62,51,201,9,217,26,24,150,174,5, +193,122,16,216,12,4,148,153,116,133,96,29,83,2,208,48,152,6,17,129,189,8,211,42,247,66,99,108,74,154,142,136, +170,19,249,80,109,93,9,23,29,42,85,45,30,64,52,128,121,246,4,1,61,124,3,128,219,140,199,246,149,154,16,12, +69,19,161,175,4,38,156,46,217,148,96,85,30,131,250,185,240,81,129,238,31,8,251,52,9,26,156,122,4,204,43,28, +153,17,122,187,232,3,217,20,7,103,115,13,221,192,35,129,51,233,24,193,62,25,134,200,230,80,58,66,168,79,66,253, +120,151,157,232,8,23,85,118,224,26,128,231,68,47,6,251,30,186,3,193,112,139,32,84,159,58,10,8,225,110,235,43, +90,45,157,48,39,41,217,15,161,215,92,154,10,191,3,8,238,9,26,4,141,5,100,87,28,182,1,212,127,172,5,254, +221,3,56,16,218,142,144,192,105,5,3,10,246,92,70,1,146,103,93,129,125,36,136,246,132,28,161,42,47,66,110,245, +111,26,7,9,29,161,85,199,0,212,15,188,32,11,4,126,117,3,10,8,166,12,232,7,114,143,2,169,174,43,44,202, +121,193,30,163,4,92,122,6,1,175,59,142,8,26,9,213,101,24,92,28,245,199,92,255,121,4,142,92,42,164,14,113, +38,232,76,156,17,206,25,82,2,98,103,93,86,4,145,85,181,29,59,101,209,50,129,86,250,24,8,5,23,47,85,128, +1,168,47,1,113,32,176,71,81,3,2,30,232,150,13,139,90,2,157,187,237,36,191,38,44,43,80,110,5,53,203,136, +101,157,208,186,2,65,98,241,8,168,26,122,108,9,43,5,215,16,170,28,131,226,230,58,202,174,41,194,16,1,138,111, +1,111,42,148,24,13,129,48,64,83,19,228,6,196,18,219,47,6,101,5,250,244,252,24,23,64,233,128,107,8,6,120, +128,182,116,3,15,241,11,176,26,71,37,58,209,22,120,206,158,38,120,3,120,13,196,236,66,1,233,149,63,7,138,9, +134,130,33,53,145,6,215,28,8,74,5,23,230,9,12,56,135,145,31,147,71,72,43,200,60,170,227,90,51,29,17,45, +138,6,36,116,96,143,77,36,23,42,49,181,0,214,2,207,20,9,51,149,15,245,20,3,8,151,5,34,213,197,108,190, +218,226,208,22,160,142,236,20,4,239,106,124,194,134,61,106,30,43,9,109,82,66,19,53,121,28,8,239,65,251,53,179, +9,84,95,211,67,84,13,252,222,153,201,67,56,64,218,44,66,48,205,19,204,72,162,141,26,3,201,85,222,7,23,105, +58,49,173,64,53,133,3,247,80,251,67,247,195,3,2,41,1,38,135,9,199,127,187,250,156,21,22,81,116,63,46,16, +54,231,102,27,202,205,243,66,188,113,164,245,9,15,218,67,35,171,8,164,9,230,50,152,211,246,21,121,179,55,70,116, +174,4,180,140,116,59,178,88,165,148,36,144,104,77,72,85,198,41,14,248,178,144,5,84,95,170,246,9,23,8,46,49, +29,212,3,88,103,1,6,15,24,62,88,73,179,38,8,201,254,231,233,131,82,64,186,106,6,150,172,71,73,80,222,16, +46,64,191,69,238,73,81,115,60,213,87,239,38,91,160,112,196,178,13,241,140,113,139,240,150,59,80,193,204,19,64,231, +196,137,161,51,35,195,26,82,6,53,196,169,5,2,160,199,250,26,23,9,8,104,181,24,161,18,64,13,33,6,68,3, +36,195,213,62,144,105,33,184,125,6,217,206,88,239,186,18,118,10,194,1,185,93,36,56,74,140,230,98,2,46,3,96, +144,255,5,95,188,204,16,9,139,124,107,42,16,54,28,19,58,197,40,142,224,129,206,220,4,36,5,124,26,35,127,30, +8,237,205,240,222,156,171,5,9,7,80,209,124,26,23,65,9,107,18,76,3,80,28,93,168,252,67,37,1,164,192,228, +169,40,155,238,56,69,19,251,6,116,159,242,21,123,68,159,228,119,149,4,134,242,119,7,132,154,47,50,62,6,9,138, +28,195,64,48,116,29,229,16,140,179,192,19,10,99,172,100,43,13,252,74,208,37,2,143,96,56,71,156,40,60,249,164, +54,19,90,194,7,53,11,236,24,26,90,23,45,8,105,18,128,106,7,255,194,1,41,1,162,130,139,248,137,69,118,10, +5,118,196,162,68,5,64,187,83,244,146,142,72,220,16,38,71,192,252,183,170,142,24,243,9,238,9,148,28,103,184,66, +249,23,227,164,187,167,16,116,154,9,105,131,120,166,164,131,62,235,185,196,224,239,197,177,1,224,166,52,156,109,142,86, +7,6,144,212,105,8,24,213,22,162,23,5,214,18,152,6,176,15,48,124,144,137,67,228,62,74,196,4,157,5,5,2, +147,252,47,5,203,108,148,88,20,88,196,161,57,11,217,67,181,33,6,116,139,3,230,121,95,191,28,117,69,234,32,92, +64,42,70,233,89,16,65,98,139,185,96,34,140,89,144,103,134,5,226,79,102,64,80,13,161,200,8,195,96,210,7,6, +26,140,9,24,214,208,23,240,192,27,130,2,114,19,226,176,103,160,103,176,80,164,122,208,37,104,101,157,6,43,37,2, +118,195,255,27,231,93,152,39,35,242,87,22,203,5,52,186,110,131,163,9,98,8,163,232,24,57,238,213,67,46,83,88, +104,176,102,32,74,55,161,186,203,3,145,166,102,10,12,226,130,144,66,134,128,216,116,7,6,132,161,230,180,24,8,88, +23,58,116,18,128,54,4,4,47,20,13,58,3,25,142,120,17,33,173,76,173,151,5,53,24,29,221,6,116,150,81,114, +188,124,236,99,179,32,254,2,252,40,118,99,219,8,116,211,101,7,7,88,198,22,168,17,130,72,189,13,185,142,29,150, +3,29,249,148,180,120,210,17,3,97,141,73,206,17,177,105,7,6,55,76,253,5,26,24,20,23,83,29,107,18,76,3, +96,62,170,231,204,234,68,77,3,1,16,73,30,196,133,6,119,45,246,80,153,24,68,87,7,126,198,0,10,46,21,6, +229,4,110,18,98,160,156,41,147,200,94,233,9,92,8,197,193,128,213,227,6,10,178,19,80,18,102,144,38,33,93,193, +48,177,43,122,100,6,175,41,188,123,206,19,29,70,107,7,18,3,90,175,26,188,5,245,23,22,24,160,27,192,228,47, +189,3,8,213,201,66,220,168,46,65,170,30,133,56,5,97,7,32,190,6,2,220,38,62,211,12,121,155,146,228,96,27, +45,161,63,221,8,100,202,36,10,160,139,127,208,85,200,109,168,228,201,3,44,230,36,70,130,59,187,180,132,221,119,77, +228,16,59,161,171,25,140,236,214,7,33,106,243,117,26,170,23,211,24,181,9,0,228,29,1,2,3,148,127,1,101,200, +26,161,236,213,96,122,202,154,5,40,7,35,16,126,203,156,19,138,5,246,31,35,104,186,36,204,232,216,8,138,128,131, +100,32,41,160,172,134,69,131,68,51,144,92,214,150,76,204,228,200,6,84,38,176,152,58,37,205,236,209,39,8,150,26, +186,24,23,141,64,57,1,242,25,43,33,49,229,200,23,32,183,201,15,110,30,230,7,192,140,116,5,241,64,223,157,50, +6,44,94,101,6,117,119,6,8,243,32,196,21,174,240,199,14,64,28,6,164,191,137,248,35,68,244,241,102,148,74,154, +187,234,6,25,4,226,79,38,44,206,202,111,102,173,110,8,26,24,154,72,180,22,0,230,7,126,212,127,36,225,101,196, +146,121,253,9,170,7,188,57,217,5,156,116,220,133,135,240,212,128,224,194,127,27,161,7,24,185,161,255,239,22,131,40, +90,141,0,107,106,7,108,24,9,55,80,148,242,164,231,94,6,129,71,188,192,168,13,108,6,215,18,217,141,233,211,86, +9,141,5,24,131,64,14,65,1,57,8,211,63,34,218,7,154,48,13,172,2,47,197,223,253,7,32,173,1,247,175,10, +236,9,245,118,1,65,23,64,156,223,240,126,25,223,211,8,65,253,16,21,142,228,171,64,23,219,2,64,45,252,78,81, +7,14,242,55,6,72,130,193,96,35,143,88,150,86,188,147,175,8,170,9,149,5,75,22,56,7,32,62,145,3,148,63, +149,142,92,50,16,30,205,245,78,154,2,61,166,5,7,64,156,137,1,68,123,113,192,180,96,187,67,38,179,150,196,67, +192,50,209,9,8,81,180,32,183,1,207,238,159,38,146,177,2,144,3,189,183,2,6,14,8,162,196,179,107,96,109,230, +16,117,196,7,74,99,108,7,166,141,229,170,24,64,0,57,129,220,31,38,167,233,50,129,36,89,155,124,64,62,166,62, +4,2,82,59,67,236,161,16,162,55,112,160,26,182,2,132,94,6,39,246,219,33,226,80,247,16,116,155,46,253,71,8, +115,119,8,124,4,94,72,14,203,7,162,8,6,99,221,146,198,57,70,33,132,56,16,227,134,181,192,65,134,121,93,24, +7,138,83,0,226,7,222,2,60,40,253,11,45,36,147,151,27,7,245,208,7,123,24,207,191,2,13,27,8,20,144,254, +178,139,162,53,135,245,112,226,143,67,174,225,177,244,9,26,162,8,60,137,1,61,203,247,2,5,6,77,55,18,8,133, +99,4,168,16,14,216,59,66,239,50,235,10,234,177,114,48,107,24,67,92,103,67,90,130,189,153,229,53,26,24,16,184, +7,96,125,225,16,126,197,176,101,73,168,247,103,222,7,132,208,49,223,174,85,62,246,7,8,193,189,60,173,129,184,145, +69,246,98,80,55,241,123,174,204,173,26,21,9,12,105,5,158,121,217,80,38,216,117,10,79,251,120,145,220,28,210,2, +133,52,196,29,75,12,100,173,49,204,29,69,147,43,28,117,132,2,90,58,119,70,7,205,9,188,64,136,7,96,30,118, +45,41,216,18,83,40,19,15,188,7,129,28,90,177,234,74,138,8,10,108,174,129,222,29,16,255,88,140,9,171,15,124, +222,157,237,26,9,147,204,81,69,181,2,221,45,38,208,172,60,8,9,7,203,5,86,24,59,216,1,239,121,9,16,156, +88,199,48,117,22,29,176,39,51,80,21,59,119,7,86,3,240,75,0,119,7,221,30,116,47,197,87,39,141,81,246,4, +9,5,6,217,92,170,250,7,8,19,58,66,49,238,61,230,17,129,24,112,9,31,101,91,199,59,9,26,74,8,219,93, +211,98,59,102,108,50,38,170,49,139,80,9,91,16,40,136,43,6,144,10,220,243,194,160,84,199,176,99,161,190,53,77, +161,182,30,20,85,56,214,8,107,9,40,7,96,210,1,3,25,2,253,176,2,11,112,194,7,48,145,5,70,17,4,52, +109,80,8,155,32,180,234,142,38,2,97,7,14,30,241,18,1,94,8,26,137,9,64,189,34,5,220,84,4,168,38,80, +117,250,16,2,9,36,5,163,44,222,6,23,6,66,2,229,149,6,189,153,192,18,243,42,109,102,169,98,13,2,169,49, +235,72,9,44,1,220,52,1,3,134,67,3,149,141,186,194,245,5,7,242,136,86,236,178,107,8,97,1,100,103,127,216, +81,35,2,106,231,133,254,9,53,184,2,71,126,163,43,166,65,210,117,164,26,225,123,46,177,92,39,161,57,32,117,20, +6,24,83,124,53,14,91,49,187,46,75,43,38,3,168,160,64,102,42,243,236,183,7,127,8,81,112,14,192,127,18,35, +178,34,2,187,16,128,243,232,7,40,53,34,17,207,3,144,219,161,8,75,3,51,105,1,68,136,9,139,158,1,115,21, +11,139,126,124,221,34,244,231,36,191,26,2,9,109,60,231,6,5,8,112,89,130,175,51,232,52,148,6,24,97,169,94, +6,73,78,3,87,161,170,27,211,84,100,136,19,9,199,32,29,59,48,7,96,63,146,2,59,234,136,144,182,179,6,181, +96,71,11,122,70,32,116,174,187,61,8,13,162,41,60,113,33,110,9,45,8,10,45,202,164,9,160,137,16,57,201,84, +9,178,2,162,1,98,108,9,25,130,176,57,194,10,6,13,24,119,85,139,21,30,60,118,114,172,213,57,193,152,21,161, +14,245,238,204,201,0,118,7,194,64,135,135,3,160,125,219,6,239,88,7,132,112,80,108,143,94,94,156,42,8,20,76, +164,217,72,111,97,144,48,106,126,75,130,117,1,172,30,140,204,87,244,58,77,234,143,38,71,69,9,128,160,139,163,174, +221,48,100,55,16,80,108,188,241,140,96,89,147,133,52,129,24,27,50,99,204,186,234,36,1,216,52,1,2,8,96,244, +103,101,5,8,32,7,224,36,227,6,106,251,200,26,118,4,177,9,69,145,112,58,4,129,169,148,203,6,5,42,1,87, +30,52,188,46,221,42,149,48,65,50,52,148,9,54,64,235,227,0,138,49,202,58,78,253,33,78,75,3,119,140,64,102, +22,110,8,174,140,230,228,1,212,31,61,4,102,1,231,167,128,96,249,35,250,65,25,169,69,255,136,23,172,5,72,145, +74,52,9,2,65,7,212,155,251,15,194,29,38,220,30,81,106,19,21,196,50,51,221,222,60,195,176,78,1,29,197,132, +2,96,184,192,106,236,4,169,24,232,5,115,108,27,45,166,32,236,42,156,80,225,70,158,64,152,7,96,58,34,50,225, +227,231,224,64,125,143,192,96,35,203,115,250,71,18,69,65,59,1,9,69,5,191,160,58,46,147,209,1,84,102,174,134, +91,66,7,49,165,0,253,184,51,48,148,186,175,48,235,168,11,112,24,23,212,200,26,145,222,200,166,254,20,224,221,28, +81,68,16,184,3,19,5,134,53,86,64,160,7,96,56,250,225,103,66,245,136,5,34,7,8,8,48,54,229,4,165,6, +2,209,21,210,30,196,166,253,69,235,26,171,58,11,160,162,133,70,107,198,31,221,30,66,22,7,174,93,9,183,48,188, +5,169,170,89,213,103,11,212,182,46,4,7,16,140,2,1,37,134,129,194,12,2,197,152,18,201,139,221,5,219,220,31, +29,143,64,115,128,58,133,2,7,208,89,56,189,108,5,34,7,8,8,18,27,7,2,140,232,80,6,45,211,134,116,142, +58,100,214,241,149,219,15,240,91,196,6,104,74,109,66,29,117,232,9,187,1,5,157,83,0,213,156,59,129,179,8,40, +30,28,83,0,150,154,178,193,3,149,27,172,3,54,45,183,1,238,86,6,181,86,174,9,16,7,80,190,1,8,90,197, +206,186,7,196,16,64,8,144,61,197,201,60,5,6,94,238,34,75,190,251,120,121,88,43,5,22,213,113,249,191,156,126, +109,73,68,6,183,8,9,67,170,125,157,11,26,84,137,67,5,198,222,30,194,5,133,90,152,64,150,10,76,72,8,96, +241,116,4,172,120,139,66,85,9,53,192,59,1,214,139,177,115,170,57,0,242,48,32,50,7,1,209,131,31,5,6,159, +4,123,30,138,155,166,170,71,145,5,131,49,12,16,2,160,230,72,4,164,182,97,129,105,2,196,9,5,243,6,103,86, +26,196,183,7,99,244,90,2,221,80,5,21,83,51,181,29,248,83,164,99,18,112,102,241,221,137,216,221,4,72,177,97, +91,214,33,64,92,3,208,62,112,179,41,7,199,196,48,56,63,184,244,95,3,186,119,143,228,39,33,80,154,139,110,64, +164,141,5,9,37,198,230,64,148,206,232,16,102,48,21,7,49,51,38,66,167,8,107,26,76,81,163,120,48,68,43,30, +225,3,1,38,106,200,44,19,21,197,26,217,44,83,35,137,83,41,197,218,69,3,141,205,64,132,3,184,62,58,15,97, +197,200,170,131,3,100,234,203,11,3,238,18,223,242,47,133,99,66,110,45,179,0,187,229,5,152,249,70,172,204,16,27, +58,7,251,38,41,140,170,108,234,177,38,9,52,12,8,1,192,213,136,37,3,37,28,209,199,27,96,127,175,91,108,222, +1,244,89,190,5,9,12,86,208,7,112,31,103,30,66,8,32,248,96,224,46,131,169,77,247,88,29,73,217,156,244,39, +86,137,2,124,129,115,46,113,128,26,213,190,7,2,44,43,6,219,174,0,236,38,5,113,112,87,40,2,56,72,3,14, +106,2,21,177,113,71,181,183,9,131,86,219,164,7,197,65,62,6,181,64,9,147,0,59,4,1,93,148,30,64,8,176, +137,4,138,182,3,37,2,137,1,62,115,5,174,96,168,221,142,156,45,187,130,226,3,74,142,1,14,208,35,110,5,118, +45,48,125,95,231,9,165,26,8,103,6,119,48,143,42,93,134,51,70,40,39,232,147,49,229,231,5,9,119,6,232,209, +9,216,7,14,240,189,64,13,84,104,1,204,15,121,235,163,30,7,8,2,0,229,36,14,209,3,20,232,95,150,231,21, +78,144,218,135,20,224,22,225,73,12,102,119,253,187,81,152,213,254,45,155,5,182,108,116,7,26,9,89,8,193,109,198, +13,207,32,89,144,47,232,4,253,43,134,192,204,81,10,12,178,5,61,134,4,196,142,7,218,190,8,64,135,188,76,9, +168,7,32,62,79,191,30,162,66,8,14,24,5,2,108,36,2,24,246,3,49,78,6,231,170,117,17,66,183,4,231,32, +27,178,196,161,78,88,3,18,200,30,225,11,3,103,232,5,8,9,103,15,193,201,104,32,200,30,5,12,211,0,225,135, +60,226,141,172,2,21,39,46,1,241,2,158,101,216,8,64,42,193,168,7,16,61,15,196,210,66,7,131,8,6,7,3, +53,227,10,2,3,25,209,61,197,114,207,192,34,66,5,139,45,80,50,31,248,204,26,146,114,9,45,92,214,10,32,193, +38,187,180,179,9,183,99,7,82,4,32,150,30,136,84,102,151,240,217,32,225,99,1,206,21,109,94,8,82,68,62,65, +3,9,8,64,134,218,141,64,55,128,242,123,30,247,240,66,8,80,124,113,175,164,2,8,13,88,54,223,226,123,139,7, +229,108,30,8,102,131,7,10,3,206,91,5,240,235,134,64,126,66,123,219,5,2,7,193,129,68,59,129,184,176,136,5, +40,30,106,65,186,131,2,225,195,2,152,209,164,4,131,142,8,253,58,87,103,196,176,64,168,7,101,192,54,133,2,246, +111,30,191,66,193,7,8,131,5,1,26,245,136,11,244,69,80,2,35,195,214,175,7,94,9,255,104,3,124,167,197,82, +65,68,186,54,65,59,132,37,5,122,45,11,217,128,138,6,185,102,128,144,70,96,221,136,18,195,164,12,12,160,74,131, +123,19,200,228,140,165,87,174,8,64,194,7,133,96,0,27,64,232,104,5,126,17,219,2,0,89,26,51,2,1,244,70, +16,239,204,96,35,110,5,168,147,58,102,220,5,56,230,5,212,144,235,23,70,102,48,53,123,173,38,223,41,2,204,228, +152,7,151,96,98,198,5,212,221,81,30,16,51,13,252,197,146,131,50,4,16,2,73,188,251,223,215,19,78,66,87,71, +179,228,80,64,248,106,56,160,26,128,238,175,5,218,125,252,66,8,20,35,4,210,157,50,140,140,98,59,65,32,35,81, +5,137,3,163,30,7,194,5,76,17,196,29,97,144,135,69,152,127,133,224,100,251,5,2,43,38,168,179,83,99,16,46, +26,226,236,241,34,3,98,32,56,209,88,3,106,136,4,64,81,52,99,137,108,26,228,104,65,87,113,36,64,44,7,120, +0,6,144,206,2,4,197,137,30,193,66,8,2,7,47,53,24,11,74,199,37,185,141,68,129,29,192,191,5,200,181,48, +68,1,124,134,59,33,125,63,171,141,248,42,255,6,5,4,38,159,3,51,1,6,31,227,154,5,129,130,140,4,28,50, +44,2,17,91,100,96,51,154,204,30,81,202,10,30,88,87,83,66,188,64,131,116,177,72,3,48,28,206,5,118,20,30, +67,8,3,198,117,100,15,216,136,39,19,70,129,33,216,107,6,3,10,22,7,66,5,63,219,3,6,104,58,200,126,224, +207,89,149,54,65,249,80,5,42,213,174,1,115,255,147,63,139,52,5,3,76,232,149,192,224,100,194,7,12,36,130,200, +125,6,1,7,8,171,166,80,246,84,87,89,24,234,64,66,7,199,128,0,101,1,102,175,215,66,116,66,67,8,8,30, +246,8,80,236,201,44,132,246,172,6,138,101,112,90,187,2,182,67,184,11,246,3,123,35,58,33,128,200,19,240,229,26, +154,153,2,120,62,6,25,3,48,96,92,136,58,209,4,133,6,3,29,2,114,48,99,8,2,216,172,42,42,5,201,27, +107,71,64,161,84,245,83,89,24,66,228,64,31,74,24,227,128,98,9,1,205,159,180,130,42,33,8,129,225,62,175,188, +145,28,128,140,65,56,52,78,210,5,61,91,183,102,15,134,3,65,215,35,138,124,50,28,179,30,200,124,152,47,7,99, +148,143,201,5,144,20,25,104,213,133,6,132,5,131,28,108,214,42,93,216,54,52,65,97,22,237,150,114,23,37,203,5, +47,71,83,89,24,67,113,64,32,171,18,0,1,132,31,97,36,28,196,139,8,12,34,30,203,233,2,160,250,160,142,129, +142,78,64,146,32,116,207,222,84,23,114,51,44,3,9,203,63,204,103,9,112,147,149,2,178,1,246,157,38,101,89,10, +88,6,7,128,186,16,27,243,12,6,5,96,245,233,9,5,80,246,146,168,221,65,220,16,79,124,49,195,156,49,216,20, +125,71,83,89,24,15,59,64,32,238,46,24,176,5,240,125,148,144,115,82,16,8,131,30,1,23,163,237,161,145,83,159, +146,9,132,25,138,69,104,216,63,120,176,45,221,229,30,168,247,236,27,118,58,218,99,139,44,149,100,35,18,15,146,122, +38,45,234,162,3,85,196,7,17,86,197,142,17,216,189,128,223,31,250,194,26,70,123,27,147,4,184,3,195,187,144,38, +23,3,94,200,56,134,119,10,6,130,199,71,83,209,89,24,84,95,7,64,97,9,99,184,0,23,128,60,224,132,8,32, +11,225,49,0,243,210,97,24,104,148,208,34,78,222,5,47,165,8,31,68,218,24,27,4,24,142,130,114,199,225,202,100, +42,200,53,232,7,4,38,186,45,2,238,11,244,186,12,11,152,175,134,243,9,24,46,135,78,3,102,140,81,145,32,232, +14,74,2,208,217,139,177,187,176,138,6,95,71,83,216,89,24,56,178,64,14,251,24,0,0,186,7,233,246,144,61,108, +8,65,30,26,169,1,1,100,103,60,17,106,6,7,52,17,2,99,5,23,210,129,67,160,186,29,243,31,58,4,23,177, +245,31,112,204,69,1,206,243,7,38,44,43,238,153,77,116,133,0,179,82,6,169,211,106,88,27,202,107,52,116,199,46, +16,68,2,222,5,168,115,170,186,15,109,119,5,6,15,162,93,249,84,83,24,15,8,0,7,64,93,36,84,9,123,24, +5,176,30,108,23,236,8,65,58,117,207,226,208,1,128,122,128,96,144,2,92,238,10,236,2,30,90,53,7,14,255,65, +119,55,5,129,137,106,179,146,216,61,162,76,68,125,7,6,38,43,2,232,211,4,218,255,234,6,29,162,150,5,160,120, +14,60,48,119,209,2,55,53,6,100,80,245,5,152,146,7,123,146,186,114,6,203,8,83,136,24,31,54,64,74,81,143, +0,0,178,23,1,231,33,52,5,249,8,33,66,42,79,210,21,12,4,226,12,18,2,5,80,67,185,231,88,135,118,187, +110,5,77,62,42,187,131,234,126,216,9,36,58,78,152,136,210,6,7,62,200,92,78,142,164,3,217,221,184,27,131,132, +6,60,48,119,242,2,220,195,4,223,3,6,119,58,1,30,102,246,240,233,164,161,101,7,196,83,24,28,8,0,64,69, +85,9,53,128,21,64,235,82,59,8,7,194,8,64,238,95,110,60,193,80,244,193,236,3,8,2,128,171,8,119,116,99, +144,239,59,2,9,10,132,126,31,214,98,168,66,93,165,3,129,72,49,207,75,152,188,5,132,4,246,49,157,199,8,9, +7,108,15,144,183,2,1,223,131,131,191,117,200,60,244,5,120,33,201,72,54,216,151,6,136,73,67,95,66,198,89,24, +24,0,86,138,146,64,192,43,3,253,210,123,138,129,5,8,13,145,126,162,60,194,58,32,59,32,47,111,2,16,21,100, +118,71,222,152,30,244,59,230,213,6,207,98,59,162,42,40,66,120,91,58,253,14,217,142,40,209,15,5,173,4,2,170, +7,72,41,89,7,205,161,219,56,131,3,66,24,100,3,4,44,30,9,4,98,6,52,97,2,239,5,9,12,40,248,239, +8,238,139,235,74,6,75,49,141,127,66,89,24,24,113,7,0,64,22,173,22,206,0,166,7,12,65,248,164,8,16,10, +237,58,86,60,24,97,58,1,3,48,40,63,116,242,181,127,243,49,33,87,243,153,4,10,29,194,18,181,219,178,134,66, +58,208,250,57,170,138,121,210,35,87,5,161,38,74,6,194,73,97,90,168,8,116,3,226,121,49,132,4,133,30,128,137, +136,5,25,222,68,28,44,2,77,4,6,11,100,7,5,164,218,40,80,102,103,194,83,24,7,150,8,188,9,0,2,152, +28,141,243,142,149,49,0,185,106,58,193,60,1,255,225,4,58,150,24,68,2,2,215,169,3,41,174,123,20,35,142,46, +30,119,109,94,68,161,66,118,107,139,217,118,65,188,49,234,7,209,45,2,122,89,69,205,47,151,5,168,132,121,3,8, +10,6,123,30,5,20,227,165,2,3,242,157,6,2,197,121,101,94,105,43,6,144,70,42,176,85,24,129,176,17,120,22, +0,5,16,56,90,31,40,233,95,53,49,124,11,166,60,8,24,58,1,65,58,64,198,24,70,2,40,251,9,226,125,20, +20,7,64,177,86,81,76,161,66,90,3,209,171,186,16,232,186,3,253,6,5,43,21,38,22,65,136,49,55,7,170,189, +61,207,15,133,200,24,80,2,40,34,193,198,102,27,176,109,67,36,213,203,5,69,140,117,6,241,147,73,98,49,207,184, +85,89,24,195,64,0,146,7,237,22,77,40,4,240,57,57,22,8,32,157,7,154,111,235,193,3,60,3,58,1,24,46, +75,81,130,128,192,61,83,250,26,168,209,253,138,17,223,218,46,214,115,242,242,101,1,16,77,118,229,39,56,249,125,7, +6,43,38,5,191,62,141,154,6,174,161,125,45,134,14,44,3,32,20,12,6,173,117,193,192,118,225,148,249,155,3,2, +231,142,218,178,110,139,198,38,130,127,3,85,89,132,24,15,110,17,118,22,36,0,2,120,11,39,73,17,8,0,180,175, +227,69,58,132,60,32,182,58,72,9,18,168,103,133,163,11,108,248,232,98,148,39,30,153,117,36,7,14,201,157,251,170, +177,168,66,111,109,3,91,135,122,199,27,243,144,234,203,5,129,231,127,242,173,68,114,173,144,230,33,3,1,128,49,160, +120,13,208,229,93,2,10,3,218,170,213,221,188,253,6,90,2,108,10,234,85,35,24,14,22,9,0,144,137,96,230,0, +77,11,3,239,95,6,161,8,32,169,222,208,2,168,130,60,48,181,58,16,46,130,105,1,160,230,17,65,123,82,34,181, +127,114,196,121,7,6,142,59,200,164,84,110,30,66,187,66,147,190,35,138,161,200,61,138,201,6,143,251,64,174,108,11, +81,129,82,48,20,216,45,88,236,220,6,10,9,64,177,133,18,14,221,240,85,115,85,98,52,121,250,6,27,40,19,10, +85,36,24,36,22,30,134,50,155,112,0,9,160,252,214,25,7,8,3,163,156,1,228,65,58,136,60,22,8,230,4,210, +5,1,106,199,174,15,181,214,34,153,22,4,115,42,133,102,3,73,87,73,49,41,180,58,17,8,106,85,152,124,7,48, +185,76,3,42,142,138,26,48,109,130,77,10,156,28,10,86,2,13,122,6,9,10,125,4,2,16,117,168,9,92,77,155, +41,84,5,142,3,236,209,98,53,1,102,5,125,85,24,90,22,65,64,194,0,24,49,206,8,25,128,33,1,243,158,7, +250,8,22,57,227,253,222,228,32,140,58,1,77,160,60,21,19,250,15,192,95,8,166,73,129,30,5,135,138,85,253,199, +100,233,166,65,245,16,34,29,227,81,84,161,143,7,45,204,33,113,103,65,22,6,218,3,0,85,138,26,84,90,3,42, +228,131,90,80,38,116,6,156,1,72,16,94,6,70,114,22,96,91,232,83,85,200,90,22,42,142,85,3,7,184,194,9, +23,237,5,152,2,24,61,133,254,54,8,7,224,238,234,2,8,22,101,12,36,58,84,80,60,161,1,166,45,152,63,25, +125,20,3,194,32,198,191,75,5,7,92,126,178,3,40,148,145,201,14,232,154,222,16,127,245,7,22,1,173,195,245,110, +7,163,21,128,115,81,2,93,218,130,160,131,26,183,0,6,10,26,161,221,131,183,183,204,4,113,41,246,4,51,154,192, +168,209,29,85,90,24,51,120,64,1,24,22,129,97,79,235,5,9,164,184,4,32,61,175,240,157,8,113,197,14,122,65, +84,50,131,133,58,20,60,216,53,15,62,35,3,112,9,236,237,204,74,5,29,6,183,5,98,231,166,3,105,88,12,118, +71,120,77,120,123,3,2,201,67,230,228,185,241,7,3,214,129,37,203,49,222,131,73,207,47,120,6,10,205,64,120,144, +216,228,5,4,164,205,173,153,246,27,215,169,51,166,131,77,24,131,85,7,1,7,22,16,111,24,1,5,9,85,21,22, +23,0,132,7,177,15,194,106,122,78,116,145,20,146,14,29,3,58,4,38,1,38,61,48,53,123,172,145,20,169,50,136, +24,232,124,199,240,152,38,202,104,39,145,137,14,253,168,149,176,63,66,245,122,75,212,134,11,185,97,41,113,6,243,58, +32,13,95,9,45,27,70,194,64,183,64,119,213,226,4,169,55,27,5,231,190,235,132,155,6,214,24,85,12,30,8,1, +24,22,65,86,245,140,138,23,152,64,0,4,0,58,91,5,241,148,17,84,3,196,7,185,49,24,114,228,200,9,55,210, +8,3,205,193,128,109,194,84,125,68,60,5,154,83,60,81,58,111,8,200,181,91,67,83,224,83,14,45,218,181,116,196, +5,49,191,16,134,4,23,60,93,1,45,53,38,169,238,64,160,83,43,16,102,199,108,185,5,52,6,55,172,107,5,103, +24,85,7,14,3,7,22,66,24,222,200,23,18,145,18,76,7,224,189,4,93,7,200,227,169,176,15,61,101,26,22,1, +12,132,229,195,1,233,128,138,247,40,183,42,200,199,212,150,179,198,69,3,216,236,200,197,64,167,108,66,150,58,166,44, +144,89,186,162,145,27,81,221,198,113,2,144,65,30,65,50,58,9,102,15,2,24,51,155,96,237,173,29,219,53,6,140, +155,6,87,225,58,184,8,85,56,105,24,0,88,22,86,9,86,23,67,24,8,113,161,18,77,44,3,192,62,244,6,253, +38,44,84,231,96,160,60,148,44,198,1,24,15,74,15,94,80,59,69,71,168,65,245,92,144,241,203,202,2,110,245,140, +84,9,102,91,7,176,169,148,88,37,8,39,7,40,97,134,67,172,32,6,189,208,30,35,13,88,45,144,120,42,163,200, +141,184,121,165,7,233,47,171,6,77,36,8,107,5,196,224,66,85,64,118,7,4,22,28,6,1,23,65,24,55,56,18, +185,232,9,88,7,112,191,2,172,95,33,98,240,4,112,26,100,14,82,16,15,66,46,208,104,162,152,190,132,57,244,17, +15,28,202,57,214,49,80,86,104,66,7,100,32,220,141,64,78,228,12,200,48,14,74,3,11,125,132,30,92,58,17,196, +86,81,227,232,16,198,184,86,114,248,177,18,6,133,152,173,150,176,85,32,61,83,1,64,24,21,9,84,176,23,170,22, +63,220,7,23,166,184,14,224,245,88,2,211,22,49,4,2,64,166,64,240,140,15,48,68,7,222,26,78,49,52,90,169, +5,244,50,143,30,238,4,161,86,48,247,8,176,7,104,42,130,176,100,147,70,156,2,83,32,88,66,2,191,193,30,7, +130,3,90,208,225,143,11,36,14,226,43,70,4,5,59,122,21,98,216,5,248,58,85,18,28,8,3,24,131,215,181,22, +23,212,52,170,46,193,183,9,128,0,238,24,5,2,25,154,143,56,12,5,149,62,73,20,241,40,64,142,23,96,146,43, +7,164,45,140,179,168,145,53,103,226,8,151,203,106,220,52,77,246,216,52,106,78,18,141,30,66,145,68,13,53,34,0, +110,56,193,128,116,206,152,19,130,65,58,237,174,216,88,107,20,60,7,3,7,22,117,6,65,20,143,58,129,85,65,4, +200,9,24,87,7,222,8,9,203,23,16,105,24,22,53,1,220,33,3,166,15,154,231,30,5,240,32,30,84,118,96,72, +30,193,14,37,163,98,91,182,181,5,4,210,43,150,179,5,143,244,67,52,102,176,7,104,129,76,184,181,58,232,66,115, +84,142,30,202,237,9,15,24,243,130,174,3,48,48,112,5,100,232,133,134,38,110,195,117,57,26,143,0,178,107,166,139, +5,110,85,5,6,24,1,144,194,9,102,226,23,13,24,22,177,0,0,138,7,224,8,1,101,2,32,238,64,128,115,136, +30,243,168,18,101,171,191,214,5,4,73,144,162,177,37,14,216,154,124,217,180,6,7,43,58,92,83,66,81,66,96,186, +164,82,6,24,142,233,88,2,132,25,192,128,44,65,128,177,13,99,166,64,221,14,24,52,90,150,105,164,189,6,22,5, +222,83,85,5,7,66,4,24,8,57,9,1,23,13,22,24,38,144,0,9,0,126,0,224,190,3,91,235,136,98,88,96, +45,24,75,149,108,59,253,5,4,152,95,107,177,7,38,87,159,38,109,6,86,5,176,128,75,44,66,40,58,88,118,22, +211,57,121,187,2,241,130,226,173,10,254,6,96,178,58,184,48,19,216,160,29,81,187,173,5,64,149,208,6,186,139,224, +24,85,80,120,29,1,8,24,195,64,0,216,22,23,46,171,92,9,0,2,88,96,2,4,32,131,234,145,142,252,96,123, +69,178,25,141,147,161,184,2,98,137,171,10,4,63,9,145,196,189,178,135,235,56,205,8,44,84,88,66,87,4,118,198, +186,30,218,36,21,78,120,192,211,146,205,81,147,16,194,146,5,2,176,48,19,152,198,26,135,201,153,7,19,97,178,5, +240,84,83,85,40,56,8,0,24,194,64,205,23,67,82,46,7,0,1,20,33,18,38,124,224,4,34,4,3,123,224,219, +90,58,38,124,81,0,110,22,72,91,102,49,229,23,155,71,49,2,92,65,144,64,42,73,66,2,205,133,30,65,146,85, +57,227,3,78,9,90,99,130,9,2,76,22,204,9,140,66,2,126,92,73,168,99,12,149,6,20,5,214,224,66,83,85, +80,120,83,0,7,24,131,64,0,208,9,29,189,23,13,93,48,8,225,18,64,19,154,61,204,132,7,130,229,16,109,48, +62,189,119,244,158,150,172,231,187,3,7,47,100,166,237,7,133,12,118,99,246,72,32,90,182,116,7,66,9,238,30,135, +89,225,72,232,63,35,0,122,214,219,122,31,8,53,192,53,35,170,8,1,50,68,217,223,25,38,254,83,195,89,90,131, +85,135,84,0,24,16,87,64,179,7,88,23,53,50,32,18,66,18,212,60,77,131,220,216,68,7,4,169,30,20,169,7, +13,60,58,130,239,231,58,43,7,33,207,212,151,4,165,7,187,100,110,78,236,150,134,164,193,28,38,212,66,40,200,138, +30,27,118,60,66,242,93,6,208,211,149,163,5,205,11,0,108,8,128,50,145,213,132,1,34,242,100,167,199,107,24,162, +83,89,166,90,85,12,23,83,121,24,161,64,138,247,213,209,22,177,23,167,9,114,133,0,40,129,243,123,155,8,7,40, +78,5,216,31,131,60,58,67,104,252,191,130,5,170,5,177,151,4,17,238,159,186,220,21,235,184,71,128,72,137,35,201, +66,114,83,88,81,173,137,60,153,100,45,198,1,82,88,66,14,157,131,187,117,199,48,54,1,38,84,6,168,129,160,9, +171,224,146,173,6,157,4,83,26,89,24,48,90,85,96,242,83,3,7,64,14,19,39,24,8,5,89,233,212,32,128,42, +5,1,245,178,6,5,16,7,194,30,14,7,218,60,193,201,125,199,241,146,7,94,207,7,61,66,238,148,2,199,245,2, +127,49,68,242,149,197,216,204,50,24,150,209,32,38,195,9,4,3,89,66,114,19,65,106,64,210,143,1,9,146,12,130, +12,248,106,134,11,138,129,190,78,5,43,187,94,24,83,89,154,19,131,85,7,83,24,1,216,7,64,42,193,204,67,241, +37,24,200,86,246,80,10,32,108,251,6,5,213,130,3,185,59,176,195,30,60,96,255,10,58,46,3,188,6,46,74,5, +209,6,99,99,56,62,50,91,67,248,19,117,230,4,94,1,173,30,115,224,200,130,205,231,227,3,6,218,10,129,217,133, +82,129,36,21,25,138,12,44,2,10,128,117,67,168,85,229,37,5,97,160,209,83,89,24,193,90,85,130,83,219,236,1, +7,64,22,24,99,9,4,155,156,5,132,2,88,65,18,83,127,92,5,97,30,24,67,66,13,30,60,131,255,231,58,122, +200,161,115,24,233,129,224,89,98,2,3,33,14,99,189,177,7,216,30,83,54,4,56,63,76,173,111,114,5,7,6,169, +5,10,3,45,2,248,186,194,2,137,0,189,23,9,34,209,235,101,220,223,136,116,24,83,89,65,85,192,163,24,114,36, +64,23,33,5,230,1,7,6,172,215,192,0,38,4,18,128,47,200,182,131,220,162,66,24,104,30,60,41,197,58,157,194, +2,113,58,117,40,9,168,10,176,44,34,28,32,203,23,99,130,145,139,174,58,121,146,50,235,131,49,32,54,232,157,5, +187,7,156,21,170,3,194,2,20,59,2,112,145,32,19,232,73,136,247,69,107,6,16,75,54,66,83,16,85,112,232,83, +89,24,185,84,20,64,46,32,197,7,0,81,9,107,8,128,0,78,11,18,12,92,93,166,9,248,117,135,189,7,241,99, +237,60,67,110,58,44,228,95,47,186,255,85,23,86,10,77,46,165,59,3,32,190,235,6,130,198,183,71,145,222,75,58, +81,23,30,119,105,48,127,224,64,8,230,240,204,13,89,136,131,2,47,131,2,131,128,63,117,200,23,17,64,22,152,5, +7,163,244,24,83,65,85,195,83,89,176,24,186,84,85,64,196,29,7,0,9,11,216,23,50,64,0,40,133,18,22,92, +16,94,100,239,58,210,73,18,30,11,12,15,122,236,82,62,38,43,139,45,240,47,10,214,161,38,183,6,111,31,150,163, +20,140,179,48,250,15,3,185,93,139,66,65,135,48,122,30,66,69,74,118,2,214,3,58,184,32,13,221,207,32,52,0, +176,194,1,2,8,1,83,130,3,82,209,145,232,172,5,129,209,83,85,6,14,83,89,24,32,186,84,113,64,23,8,215, +0,9,144,23,89,16,0,10,97,18,97,92,16,1,45,192,221,173,44,135,46,43,45,111,147,93,102,30,38,5,0,6, +154,93,2,8,7,250,107,168,144,125,13,132,47,58,83,30,66,101,39,170,186,36,60,148,241,3,4,133,60,24,58,1, +96,106,198,72,30,1,74,144,18,108,214,3,10,141,74,239,70,66,146,7,111,24,83,85,6,14,83,89,24,48,186,84, +210,9,98,139,215,8,74,5,233,58,176,0,10,193,18,16,0,147,95,45,208,44,47,159,46,38,0,74,43,179,53,41, +18,4,29,5,2,8,20,7,216,113,4,12,2,119,168,177,204,86,112,107,30,91,58,108,184,66,30,149,148,144,84,136, +23,112,16,220,16,186,2,96,224,82,5,0,86,2,40,188,2,168,93,222,222,6,160,87,96,83,85,193,83,195,89,24, +7,168,201,6,183,4,93,60,7,9,167,6,135,0,170,30,201,238,160,20,66,1,48,3,91,244,38,44,47,39,46,89, +154,41,75,105,51,176,4,177,8,40,9,84,131,231,197,5,141,191,158,3,202,169,94,35,215,207,30,66,218,97,36,217, +24,19,110,3,106,64,69,165,182,10,22,3,32,192,183,3,240,149,220,125,4,9,54,188,7,83,85,24,52,83,89,122, +24,7,4,66,140,138,58,181,9,193,129,237,240,18,79,215,199,153,7,240,10,96,114,192,192,20,190,43,44,47,146,149, +38,246,45,41,102,20,40,103,45,21,76,194,26,165,8,239,2,12,5,4,92,239,253,35,209,4,58,17,20,66,14,89, +187,140,4,44,30,4,92,3,58,133,194,11,2,0,204,13,4,59,1,3,124,11,132,6,97,166,189,7,44,156,174,83, +85,8,20,83,77,64,213,134,3,89,141,83,24,208,168,6,10,31,64,1,95,96,97,150,98,19,6,145,220,7,208,12, +193,43,209,41,44,45,106,155,45,47,133,40,29,45,1,9,248,84,26,62,82,183,4,232,48,90,5,187,251,15,33,238, +34,48,23,46,66,60,47,87,219,97,4,24,80,30,187,154,88,12,6,233,2,96,176,30,5,112,80,69,236,28,13,13, +7,35,99,70,71,83,136,85,129,150,84,41,219,6,218,173,4,72,124,7,0,2,9,24,97,64,98,55,161,28,178,43, +22,187,125,0,18,32,9,160,22,168,21,4,4,103,43,41,44,69,45,170,107,48,216,47,40,53,191,9,118,26,248,213, +8,95,85,12,85,250,7,30,5,241,6,110,152,146,2,41,47,58,30,132,66,148,3,132,4,48,138,58,188,42,181,157, +22,10,134,24,58,3,65,2,7,0,225,1,112,130,89,81,9,18,56,71,83,193,121,3,62,4,246,90,7,246,8,220, +85,132,24,225,149,64,72,42,196,108,90,7,105,22,23,42,154,55,9,7,195,164,18,1,48,5,174,61,69,51,182,38, +5,155,236,38,44,42,41,244,43,45,0,151,216,47,40,51,16,190,30,157,8,246,42,84,107,71,66,85,7,123,229,5, +138,20,115,20,150,190,60,66,34,4,153,6,244,249,33,80,123,32,172,162,96,87,3,132,3,24,1,215,153,64,193,214, +72,118,77,71,83,130,140,229,61,9,22,23,24,159,109,198,85,11,189,173,238,91,64,24,5,24,158,7,3,22,23,16, +76,9,22,32,18,152,4,176,22,193,247,197,196,187,82,96,77,244,41,44,100,39,51,138,45,148,47,80,40,177,199,92, +174,5,24,7,189,8,130,183,84,237,33,113,22,62,2,51,108,139,4,172,66,6,84,3,154,64,85,144,4,48,32,3, +192,5,51,4,155,121,136,7,141,142,65,177,79,7,24,22,23,79,6,0,225,71,83,85,5,83,25,24,4,198,171,64, +17,17,1,90,23,48,239,22,9,7,198,208,34,18,2,63,233,237,1,180,5,174,96,176,33,129,80,232,41,44,250,39, +40,51,38,45,250,41,0,125,47,40,38,93,45,50,58,5,175,172,255,157,8,66,89,68,252,108,88,8,80,5,204,40, +244,46,71,208,58,30,30,36,136,1,96,13,97,65,169,53,110,252,12,6,236,48,3,217,32,57,0,105,93,8,24,20, +23,41,9,240,0,4,99,85,64,161,83,238,182,0,229,91,64,17,12,153,22,23,2,225,7,1,47,245,254,43,128,220, +7,100,115,208,140,37,168,35,42,38,244,45,47,40,85,51,211,38,43,70,41,47,235,251,51,40,100,48,131,7,167,24, +8,127,219,239,17,172,1,172,23,162,5,2,56,226,67,115,30,31,41,45,131,194,129,30,69,177,8,3,130,4,5,129, +86,64,179,13,160,30,19,58,48,21,62,26,29,194,23,194,24,243,156,83,99,101,65,85,97,83,68,66,109,123,3,8, +64,7,143,126,98,149,7,247,22,24,198,11,108,6,254,216,18,13,57,7,163,1,180,35,1,217,6,93,4,156,135,45, +47,43,99,81,40,51,235,44,38,43,168,147,175,51,247,39,40,102,100,71,7,24,83,8,66,86,30,247,125,1,68,106, +0,107,160,155,5,53,241,2,197,197,30,149,137,1,254,215,3,126,248,125,208,3,112,53,224,224,27,7,80,48,27,96, +48,52,30,66,7,83,7,162,29,23,133,45,24,157,71,99,16,101,131,83,8,18,231,221,206,84,66,57,64,74,6,162, +5,70,179,197,110,5,64,157,24,241,64,144,163,18,34,92,137,32,55,37,194,1,188,2,203,199,194,47,1,55,245,46, +47,44,43,211,86,41,181,45,106,38,237,186,51,60,40,53,102,100,227,53,24,59,4,66,71,21,93,1,68,238,149,8, +18,69,9,88,5,161,144,71,183,58,161,87,135,240,242,194,96,42,48,16,2,2,69,16,1,10,67,1,39,4,2,3, +30,66,32,58,197,236,29,47,24,244,232,65,103,99,136,101,50,24,66,174,3,178,31,106,83,133,171,4,37,7,226,3, +1,9,10,8,233,72,23,21,52,18,128,240,206,9,26,106,3,120,15,60,84,242,40,47,40,3,155,232,44,47,234,41, +38,45,175,43,17,254,45,41,39,40,142,53,104,48,254,169,8,200,117,4,66,148,124,58,68,204,250,71,8,31,13,99, +14,64,174,64,41,99,3,64,21,88,48,69,2,246,7,128,175,32,142,36,2,58,4,14,18,66,21,30,204,98,122,8, +68,75,104,60,103,144,99,199,24,30,17,9,235,23,22,7,228,66,83,34,71,206,118,9,64,10,33,208,176,23,161,24, +183,32,176,18,2,255,110,5,27,236,190,104,13,225,2,60,135,20,73,1,119,137,189,45,30,47,44,43,38,162,19,103, +91,43,44,69,40,30,56,53,102,0,248,23,71,181,212,58,69,4,213,47,246,60,143,96,209,26,5,4,15,228,200,12, +3,239,114,134,177,96,64,128,2,159,131,23,64,64,10,26,4,10,24,30,66,128,125,191,58,105,103,87,65,172,66,21, +175,8,23,70,22,24,251,155,71,83,11,29,141,5,42,36,64,53,9,3,156,10,24,61,8,0,42,36,24,34,18,63, +235,236,238,7,24,21,9,246,94,222,3,104,80,1,60,120,69,143,200,46,13,38,44,21,47,210,179,43,34,45,134,150, +40,17,51,93,53,24,71,53,58,68,31,140,147,45,8,103,206,81,26,26,5,25,132,6,40,57,209,125,170,192,82,172, +120,3,96,15,110,19,32,96,45,203,224,59,64,118,34,8,4,40,98,30,66,5,139,212,106,18,227,9,216,22,23,41, +24,181,7,176,83,184,84,71,18,99,244,3,9,64,38,248,54,24,11,130,126,226,4,5,51,7,23,12,29,138,48,8, +0,27,64,241,15,28,17,131,201,15,128,131,45,47,70,38,43,189,46,54,171,180,162,40,39,37,157,8,24,71,204,162, +30,60,154,171,181,234,66,0,71,163,26,9,160,5,111,234,183,96,109,121,250,8,0,155,105,24,48,45,65,193,194,7, +135,32,12,210,4,16,108,30,66,42,65,86,8,124,178,10,236,26,23,34,24,25,131,111,84,83,74,58,221,234,24,7, +119,60,87,84,29,125,64,9,10,59,101,155,99,217,174,229,201,6,152,5,23,96,231,176,192,5,0,109,3,164,30,46, +76,140,58,176,76,250,43,130,52,47,145,38,85,133,44,176,87,126,3,24,71,4,60,66,165,30,83,5,24,190,8,122, +104,156,9,132,196,150,144,3,42,141,255,141,67,249,92,37,35,28,96,87,65,4,192,78,129,188,57,160,160,100,1,151, +134,253,30,228,66,7,8,2,184,45,24,144,23,85,129,66,30,107,187,9,22,24,62,107,87,194,84,238,246,7,64,15, +121,67,159,23,27,148,26,88,91,80,192,175,50,0,54,1,212,143,72,169,48,122,192,38,134,47,140,46,43,68,90,41, +185,229,61,66,71,37,170,65,122,30,1,58,8,151,7,205,58,58,9,21,194,2,4,230,14,120,48,18,176,190,22,13, +107,148,129,166,21,146,30,24,53,6,1,61,138,56,27,27,20,128,35,17,194,95,66,235,8,32,23,186,186,24,23,76, +34,5,173,181,6,166,22,248,88,65,87,193,127,0,3,6,64,32,237,228,23,28,217,9,253,80,155,29,24,76,9,145, +228,1,172,5,196,32,124,224,203,64,59,21,253,45,44,38,46,37,171,43,182,79,176,151,190,178,207,186,171,22,24,163, +20,158,138,9,40,5,160,2,26,191,58,60,64,249,132,248,125,8,4,205,102,5,17,142,59,109,30,16,7,131,30,4, +2,128,80,2,0,124,125,217,3,132,5,195,61,148,142,63,149,123,8,24,9,53,3,91,23,10,22,190,7,0,66,103, +194,87,195,71,60,19,29,9,64,14,232,175,68,24,176,117,164,22,217,29,14,24,30,1,247,165,88,192,52,132,18,226, +204,7,168,104,25,109,4,106,161,145,45,95,46,38,64,121,250,7,148,49,106,65,111,30,66,109,85,209,77,248,116,9, +95,5,216,208,16,3,145,41,145,114,2,3,10,141,106,196,10,3,1,183,194,30,7,7,124,5,129,110,24,59,72,1, +45,40,80,97,68,66,5,137,224,66,30,3,141,118,24,23,10,35,9,0,200,66,103,113,87,84,60,192,210,66,69,6, +236,125,24,22,23,39,9,175,2,7,23,33,29,97,7,31,238,5,108,184,13,33,18,61,33,226,98,49,112,67,8,54, +196,81,244,208,45,38,29,124,3,72,235,7,4,116,8,65,30,175,68,146,75,164,7,66,5,135,122,24,7,32,36,159, +238,123,72,13,107,101,13,65,128,55,75,6,186,77,112,112,129,3,128,84,78,58,4,48,57,40,40,59,137,7,15,66, +30,4,58,27,37,236,23,30,223,9,254,0,30,103,87,109,65,4,39,171,7,38,65,87,140,88,60,71,116,6,7,9, +215,98,92,8,210,23,29,28,7,64,135,1,214,76,133,58,158,179,176,127,64,76,128,154,247,254,9,85,65,240,83,239, +66,71,7,148,253,133,81,8,32,179,191,135,2,54,28,211,183,42,13,2,193,64,25,196,58,164,65,129,60,47,218,6, +6,80,107,64,240,116,3,8,58,88,50,197,48,64,222,30,5,187,66,40,141,122,250,24,23,85,7,210,194,5,1,66, +9,247,198,120,87,194,66,46,10,5,182,2,43,150,66,29,41,125,6,26,144,6,240,194,1,58,235,103,12,197,194,232, +77,118,32,23,94,223,9,47,66,1,4,65,214,8,24,223,26,9,41,133,8,131,33,206,3,154,15,160,229,86,6,203, +193,34,3,124,7,56,18,245,195,6,160,80,64,3,192,3,221,194,235,88,8,83,66,69,91,116,130,188,155,150,24,23, +65,86,2,29,238,120,58,87,194,60,38,143,140,149,6,143,22,45,5,39,9,93,39,189,0,216,0,248,73,223,194,58, +45,3,230,85,120,2,59,152,199,196,2,3,164,89,135,7,24,68,93,239,175,112,73,129,196,237,2,224,83,33,243,23, +41,5,34,2,20,35,3,60,16,51,131,1,104,6,129,176,21,226,0,222,32,4,72,5,0,176,58,187,91,3,72,53, +171,196,23,20,29,18,104,24,110,242,65,87,17,110,192,89,58,51,2,7,22,156,26,72,220,24,122,9,224,56,4,1, +61,161,224,135,58,234,18,128,34,178,2,9,181,18,117,7,105,71,189,242,8,249,107,33,86,12,121,5,205,33,2,108, +42,60,32,116,85,6,199,5,16,80,210,2,87,158,0,130,2,230,0,144,6,204,128,222,1,98,1,100,2,241,7,93, +91,96,26,213,240,29,23,195,24,9,109,236,107,87,34,109,155,106,189,103,42,58,241,7,100,196,46,24,110,26,8,75, +85,13,176,26,128,61,178,199,134,58,51,14,2,48,130,2,184,47,2,8,227,174,23,150,7,4,181,8,74,30,132,117, +196,7,16,119,201,3,251,53,50,118,6,43,193,86,5,52,139,3,122,144,128,2,169,138,155,91,8,4,16,20,30,15, +38,15,9,44,194,107,91,32,18,222,7,29,23,32,254,24,64,7,0,58,103,87,137,109,148,149,9,210,111,118,65,58, +248,226,253,150,24,179,9,194,247,180,42,3,88,67,1,199,15,12,58,50,194,228,27,49,228,108,160,217,97,79,4,8, +73,241,21,172,1,24,88,7,29,8,250,226,58,5,204,124,6,80,110,116,9,148,128,95,195,213,2,2,175,32,225,58, +45,16,234,181,3,113,144,112,176,30,50,160,216,90,2,111,91,32,86,153,252,24,23,130,182,9,198,65,103,137,109,75, +9,7,99,246,112,105,87,20,111,69,66,250,91,39,26,174,9,3,136,7,142,44,206,25,143,244,18,97,112,146,10,224, +78,99,149,56,197,58,8,46,7,146,161,3,84,19,241,1,6,54,162,58,60,212,184,57,199,46,99,52,153,10,180,2, +253,187,224,73,118,17,2,130,226,2,136,56,6,184,23,28,129,4,65,30,96,212,57,101,198,1,55,208,91,66,188,58, +173,87,24,23,74,9,134,126,110,105,65,98,218,23,157,109,105,26,87,111,27,100,28,87,25,199,147,5,95,64,6,208, +133,1,121,89,157,160,69,139,164,183,92,140,46,144,244,101,52,28,164,75,44,8,58,26,24,71,53,7,66,210,38,174, +141,3,5,217,131,113,156,242,7,48,124,27,6,101,218,7,242,121,66,183,64,2,128,128,1,140,0,184,241,144,3,2, +3,19,2,0,173,35,66,188,155,223,8,9,7,102,201,60,1,154,64,9,10,114,105,24,109,60,152,232,24,23,168,29, +137,9,99,5,35,128,214,6,250,22,18,206,17,151,2,246,54,71,190,99,181,4,115,92,71,190,73,20,167,7,222,9, +26,193,71,149,4,157,30,66,8,20,7,115,5,2,1,10,28,195,166,217,140,7,9,183,6,88,19,164,113,6,148,13, +0,254,129,192,189,144,121,84,2,228,64,115,132,58,76,132,236,13,27,66,161,30,113,44,122,248,224,188,8,22,64,9, +153,112,227,105,111,65,151,87,24,23,69,29,68,21,76,43,8,42,3,104,7,137,217,2,193,61,223,184,22,252,179,15, +57,126,233,45,54,67,200,36,251,7,8,26,216,107,30,186,204,106,66,27,41,34,5,238,135,4,46,242,85,6,185,72, +64,216,157,5,216,92,232,50,33,82,131,128,126,192,193,30,96,14,129,130,48,96,152,9,76,8,101,58,44,77,88,52, +129,139,123,114,131,127,86,24,194,23,133,104,9,182,4,176,60,58,191,122,7,29,23,72,149,24,71,120,153,94,3,104, +66,1,220,89,2,194,48,37,98,218,2,213,29,248,213,46,223,42,200,215,4,8,235,26,24,71,170,158,68,4,249,29, +66,7,8,36,102,5,2,56,128,164,142,178,7,100,212,33,30,116,160,5,187,178,220,210,144,38,140,144,112,128,4,136, +25,3,1,0,92,157,0,171,68,53,58,1,103,128,209,241,66,2,229,105,190,232,24,23,78,9,135,120,24,74,5,76, +43,29,55,146,7,156,6,242,0,218,24,1,58,238,139,159,48,77,243,81,237,129,42,219,1,250,50,179,40,110,73,222, +8,194,158,1,65,181,71,242,214,73,26,7,8,71,129,0,213,247,3,190,57,170,8,25,104,5,31,129,106,74,52,164, +10,3,0,99,3,248,3,192,211,196,1,0,216,64,34,210,45,58,128,169,1,163,176,30,66,128,234,216,95,5,19,9, +7,10,19,4,9,132,23,5,74,14,252,7,16,58,209,220,215,37,135,191,64,123,193,181,43,129,22,11,169,119,84,157, +8,221,68,71,138,169,174,249,75,215,58,177,7,22,44,133,50,185,146,102,77,203,113,168,7,43,156,23,109,5,108,22, +10,154,215,28,116,224,224,219,129,1,184,107,190,3,2,3,98,65,29,135,28,53,100,139,58,1,46,30,108,20,44,66, +221,84,16,56,135,6,150,7,142,167,5,8,24,132,23,26,29,24,56,9,7,0,14,128,118,98,1,122,139,82,83,59, +242,155,81,28,26,208,202,77,144,111,62,7,68,71,60,178,104,124,132,171,8,164,170,136,149,5,130,218,2,75,156,200, +131,128,133,242,4,1,183,5,9,67,142,177,174,80,94,199,200,48,1,31,18,115,128,144,200,58,1,4,220,200,136,14, +62,18,225,62,32,37,4,49,30,7,5,66,114,120,17,195,7,198,26,23,15,105,118,0,58,132,1,119,6,163,218,5, +130,148,224,2,141,4,177,64,203,102,80,111,42,255,68,71,1,58,30,65,60,85,157,53,244,66,8,98,7,5,88,89, +208,6,155,162,241,73,174,251,238,231,4,8,33,66,27,3,151,26,210,40,247,196,131,68,2,94,132,224,129,0,69,45, +58,176,1,6,196,8,4,192,172,13,80,79,212,5,72,7,98,66,30,60,55,225,148,183,7,24,23,71,22,8,6,106, +3,160,26,113,0,87,165,74,35,204,1,243,226,1,151,4,63,167,25,109,119,3,68,71,228,110,66,65,153,138,216,60, +8,78,7,240,254,177,232,6,174,2,231,25,43,6,98,5,145,96,38,108,249,1,9,237,32,23,120,136,1,27,12,24, +6,153,122,22,22,33,132,77,58,128,54,36,128,214,70,58,248,107,130,81,6,162,5,7,32,141,66,160,236,217,7,6, +205,192,127,3,246,65,148,222,84,40,14,250,204,36,132,64,250,6,234,87,31,211,140,23,78,68,71,58,97,104,60,222, +0,30,233,8,96,171,121,6,63,165,154,28,82,121,253,34,156,253,196,33,5,115,19,49,8,11,172,68,0,131,130,155, +248,96,15,202,11,4,169,2,85,58,3,102,57,160,213,160,196,21,70,192,18,116,5,72,7,66,91,100,45,136,163,129, +40,153,64,115,3,76,152,239,98,187,137,33,222,218,11,70,160,209,98,190,193,218,13,121,41,93,6,5,149,27,8,71, +99,65,30,81,248,219,68,8,97,129,142,142,6,45,156,228,217,7,5,53,178,252,5,91,5,154,46,7,165,6,182,31, +73,155,12,2,235,32,50,201,130,110,200,6,110,66,133,16,194,1,32,126,193,167,72,28,21,1,78,6,149,6,130,224, +128,95,26,142,188,112,166,216,3,144,29,250,5,94,8,136,87,161,236,5,192,226,44,90,151,48,151,141,238,5,135,87, +66,68,170,30,149,60,181,58,233,7,8,0,48,88,57,8,2,137,83,7,5,138,6,97,209,110,22,44,209,5,113,14, +90,63,194,1,221,135,170,134,2,152,235,136,0,131,181,44,3,7,201,155,91,56,114,78,160,159,202,177,169,153,1,241, +43,109,129,96,107,130,198,97,1,144,3,144,62,216,184,8,4,131,0,174,225,108,199,131,137,66,140,193,186,12,155,5, +112,182,68,8,165,30,69,186,60,65,66,50,1,8,17,5,57,144,117,75,209,133,28,240,161,101,7,162,142,33,84,125, +69,208,37,198,176,63,129,10,29,220,108,192,66,2,204,48,208,4,91,152,165,163,1,88,100,19,20,160,74,80,27,209, +68,28,27,118,0,174,38,5,7,145,106,113,160,28,128,229,196,97,87,147,132,23,58,238,157,168,247,79,216,30,96,38, +135,140,110,129,189,148,214,212,8,71,214,30,65,77,101,57,66,1,8,9,5,198,197,58,69,117,40,6,77,201,11,245, +244,6,3,72,208,32,125,228,5,84,124,4,3,80,51,212,1,31,39,9,234,4,69,161,91,100,20,5,48,96,70,12, +193,41,64,95,165,17,2,225,0,38,10,114,38,6,194,3,128,11,46,35,138,202,22,184,120,107,56,89,75,65,39,58, +44,201,237,9,69,161,123,26,122,8,71,149,66,232,30,161,68,183,8,33,6,30,36,235,153,213,193,177,182,175,164,145, +25,14,197,148,30,234,123,68,64,247,11,5,131,31,133,128,74,202,134,248,59,228,91,144,20,56,163,134,1,205,2,72, +182,193,64,158,93,158,198,114,77,5,6,180,3,144,7,160,22,24,200,32,18,163,164,10,244,35,149,98,22,103,164,101, +46,27,221,30,155,8,68,103,82,66,173,30,121,0,196,78,36,137,4,152,47,99,198,90,245,4,5,10,71,219,5,98, +28,209,103,116,220,233,6,4,32,19,106,38,33,237,199,113,29,88,9,96,55,232,4,40,54,226,1,208,6,13,161,132, +2,74,135,48,18,146,99,174,70,144,7,124,54,216,14,64,110,162,189,199,58,154,36,64,45,25,84,61,225,60,123,19, +120,5,41,33,165,5,102,210,8,68,99,85,30,61,203,12,34,5,247,22,216,122,193,126,199,6,42,219,112,142,104,217, +52,10,208,5,59,8,245,230,204,48,23,219,87,234,42,2,118,205,243,136,65,4,1,168,224,148,32,206,33,195,7,143, +32,168,39,236,3,223,227,7,9,30,200,0,114,3,191,54,86,179,58,133,46,116,237,72,244,58,63,244,108,151,18,146, +144,178,19,125,65,41,193,115,198,85,5,186,235,30,68,24,182,40,66,226,0,7,8,11,5,159,2,20,29,30,2,140, +106,4,89,7,155,196,6,216,199,9,160,126,3,232,11,229,197,64,10,59,49,64,193,4,0,231,102,20,206,172,24,4, +200,116,173,227,239,137,105,186,49,162,7,3,194,3,168,61,96,222,153,38,58,218,79,64,134,12,242,138,229,129,3,147, +19,22,106,185,3,238,50,173,5,23,237,8,24,71,170,66,88,30,8,194,6,221,159,146,233,30,20,209,52,236,92,246, +98,4,167,214,110,213,185,6,136,29,73,223,138,188,45,251,64,183,160,41,129,138,123,144,40,28,85,5,186,224,160,26, +209,0,121,73,168,200,6,7,29,152,14,161,1,131,2,87,11,204,67,73,58,225,109,70,164,146,65,204,93,156,107,194, +124,209,30,5,0,95,8,24,96,163,37,68,65,131,136,126,220,112,230,137,169,182,201,99,29,158,6,78,86,5,194,6, +184,127,99,176,15,122,93,196,144,10,160,56,88,4,32,24,246,2,161,16,2,176,17,114,2,181,173,6,234,117,2,164, +7,13,220,7,80,142,1,208,74,61,106,31,34,96,94,227,49,156,105,66,3,77,113,151,58,13,195,30,5,178,7,227, +8,24,5,198,7,24,47,195,133,17,103,52,76,212,16,53,145,2,22,199,144,5,127,72,206,89,33,144,29,244,150,2, +206,208,112,239,168,69,16,237,78,104,61,96,215,4,176,129,192,80,199,97,7,16,77,91,128,7,80,54,144,243,131,212, +36,5,237,58,31,185,44,171,17,1,96,235,189,141,160,202,26,244,94,5,220,203,8,136,24,46,30,90,90,7,8,27, +147,36,118,67,106,169,41,55,0,162,134,133,96,246,238,212,129,98,63,129,38,62,97,60,157,224,11,123,83,16,113,118, +64,202,55,68,7,174,12,27,182,240,91,117,41,9,200,7,27,168,14,192,29,152,237,207,80,209,23,5,60,7,220,69, +222,58,240,81,66,173,177,166,254,177,22,169,82,34,239,5,110,127,118,9,24,67,66,1,166,8,146,149,7,91,151,213, +21,19,160,128,157,76,11,43,154,203,4,80,117,68,124,6,5,212,136,31,194,11,202,59,19,140,2,224,25,16,40,44, +2,33,1,237,74,1,158,172,5,231,175,150,200,218,1,216,33,1,102,227,162,24,199,140,171,162,60,4,192,69,178,197, +58,179,40,13,56,3,204,84,53,55,5,183,55,59,9,24,14,8,0,4,155,7,85,184,79,6,2,30,104,230,49,139, +169,61,114,152,122,198,99,20,76,100,15,209,169,117,32,78,109,48,1,135,109,3,4,1,228,75,153,83,108,7,2,123, +222,39,8,24,43,64,118,11,1,0,174,224,137,143,181,26,58,60,14,10,38,162,232,109,178,140,14,72,51,4,226,34, +239,5,113,119,59,9,24,5,94,243,66,92,230,141,198,5,2,184,245,109,147,84,197,182,2,83,24,205,137,3,56,72, +26,4,196,97,103,3,35,146,24,21,194,108,3,24,69,2,34,110,33,131,196,140,34,1,163,108,164,199,100,215,7,5, +18,96,58,132,1,128,173,12,32,2,170,7,171,208,58,60,224,40,36,79,42,205,182,11,144,61,210,3,152,212,142,31, +5,7,183,8,137,208,24,187,111,29,4,235,71,58,66,165,7,65,116,52,226,210,50,51,26,100,198,51,97,251,170,7, +205,5,54,38,165,153,40,214,9,121,159,46,192,50,129,139,120,211,114,4,6,193,76,119,119,196,182,6,234,7,69,2, +90,3,168,72,1,7,102,5,76,122,180,80,60,87,74,58,74,51,31,213,152,20,218,185,210,3,254,53,35,136,208,5, +7,235,169,24,65,63,71,60,221,68,0,149,66,185,19,161,128,27,163,48,179,160,198,58,183,0,178,40,7,124,41,178, +18,69,113,115,218,0,211,170,125,152,4,56,72,3,26,212,144,115,42,5,21,59,100,26,208,28,194,1,64,43,98,194, +226,65,76,223,80,35,104,60,31,185,44,100,216,58,51,144,104,227,136,161,188,148,244,5,7,58,253,30,24,8,74,4, +125,66,58,68,162,137,47,7,152,1,243,0,55,211,155,131,12,61,59,186,104,24,27,217,150,32,21,196,6,253,188,2, +122,176,182,4,13,41,184,216,4,138,154,195,90,3,160,28,200,239,134,3,206,13,96,118,210,186,60,87,139,106,73,174, +58,81,96,39,134,142,82,225,48,27,29,5,7,14,186,250,24,30,181,66,28,253,8,0,39,94,30,44,144,213,14,156, +234,80,8,51,122,238,214,41,50,180,4,191,53,29,212,43,136,14,59,140,2,4,48,22,156,162,111,209,6,115,146,128, +117,3,58,61,232,92,3,172,21,176,91,96,215,139,106,74,148,58,172,60,6,236,120,195,96,54,52,5,95,7,234,189, +68,43,8,7,19,66,1,93,219,208,30,224,10,64,110,162,206,1,6,111,77,72,138,101,5,191,148,216,224,132,128,235, +185,22,153,49,91,200,47,6,11,165,96,29,193,230,152,99,60,4,7,212,30,112,254,212,136,11,99,58,26,246,148,58, +18,133,188,94,60,18,131,234,34,135,222,83,162,5,7,236,23,242,8,1,66,71,201,54,122,66,248,30,7,154,32,110, +233,146,78,124,154,23,24,205,208,202,93,168,192,210,5,45,58,25,54,3,192,15,128,111,42,0,255,72,60,196,32,26, +50,2,123,251,135,159,6,189,166,58,60,206,61,226,246,60,132,26,57,68,160,187,202,127,30,5,7,3,0,94,8,197, +137,68,78,65,66,84,33,114,106,10,6,105,149,144,231,138,20,138,161,37,2,67,43,20,128,86,65,47,8,147,8,3, +214,236,223,215,1,252,3,204,6,3,231,126,152,71,198,3,200,35,154,227,58,60,106,145,77,161,3,134,33,112,212,141, +206,5,141,2,0,121,8,21,206,117,19,186,30,204,88,2,231,139,206,2,3,105,76,241,40,150,204,188,2,170,65,36, +19,120,241,99,225,178,60,155,65,132,1,228,13,213,12,80,31,40,206,94,60,192,32,109,29,124,3,35,199,104,84,53, +193,56,44,204,83,25,162,235,4,82,55,58,5,53,2,0,233,7,213,66,100,117,119,8,68,94,30,249,197,161,118,65, +132,125,214,102,195,81,87,179,8,84,9,80,120,1,154,17,21,58,146,41,172,1,3,137,237,139,128,253,70,44,192,32, +109,157,124,3,35,197,196,89,6,188,101,29,176,43,211,64,157,139,144,78,123,30,5,223,10,212,236,8,138,246,60,66, +68,225,4,241,230,66,114,93,100,25,246,165,35,161,77,86,41,36,19,86,104,146,36,4,74,6,56,3,225,1,11,220, +129,223,157,116,3,24,246,40,35,22,161,148,120,193,20,67,3,130,14,59,30,5,106,81,94,8,104,85,172,4,165,66, +84,9,248,187,12,149,152,8,210,145,164,155,2,56,134,5,192,76,169,168,146,60,88,43,58,84,0,130,1,251,15,148, +236,80,184,96,38,37,152,209,62,65,171,103,12,198,120,3,203,46,65,169,181,5,116,81,30,90,60,234,214,68,171,71, +8,80,57,49,185,161,194,67,25,200,41,10,200,202,152,134,5,192,134,239,58,160,60,76,196,248,0,248,31,105,58,246, +176,30,60,135,90,176,97,1,13,152,146,61,204,195,0,212,16,116,225,3,187,36,18,157,30,5,171,162,38,13,174,68, +65,30,181,66,228,94,52,95,165,82,100,132,8,201,58,160,92,196,132,49,52,59,33,77,147,58,60,64,143,52,32,15, +128,126,180,30,30,83,176,96,184,96,8,208,164,125,207,224,192,124,97,24,25,75,133,32,100,109,5,93,18,85,58,46, +65,66,68,224,40,104,38,5,188,191,24,227,30,212,39,195,101,41,17,131,1,209,8,141,58,60,34,154,198,132,1,224, +30,102,47,70,11,29,122,9,225,4,1,108,131,104,125,215,9,120,184,225,58,24,27,201,114,96,36,232,5,107,30,117, +147,98,88,66,69,8,66,241,230,136,110,201,136,133,36,58,163,64,43,51,42,132,224,192,158,144,151,208,60,36,214,116, +64,31,0,253,36,30,5,175,81,253,97,128,76,136,167,212,94,236,80,214,151,240,204,9,232,78,67,50,5,134,179,7, +50,182,8,65,181,83,75,0,201,205,7,64,229,4,231,3,65,146,168,58,81,29,153,150,3,204,16,20,158,4,110,212, +60,20,203,162,192,15,129,244,207,3,4,99,5,142,185,4,241,32,122,4,192,91,224,148,42,125,215,16,21,12,6,97, +253,117,67,80,208,200,237,5,7,109,8,186,66,213,24,181,60,83,153,173,7,148,67,2,176,50,136,107,58,2,129,80, +86,174,102,72,6,120,78,215,60,28,133,4,144,1,224,62,111,194,62,108,54,5,3,221,2,250,68,242,131,32,152,182, +144,63,66,14,43,132,48,23,170,6,105,125,132,3,161,33,185,209,5,7,106,157,30,63,29,4,68,8,90,84,189,66, +10,150,130,175,46,200,101,50,55,58,60,105,32,101,22,67,7,170,13,89,47,58,60,101,33,4,172,58,0,248,15,201, +20,30,86,5,22,132,51,208,176,183,136,110,16,177,28,182,67,140,202,48,92,18,4,191,4,197,102,97,168,162,5,7, +215,3,8,82,24,78,91,0,30,168,66,221,37,3,53,234,22,172,171,26,228,226,58,73,1,13,58,41,140,10,67,77, +158,60,130,131,41,160,0,124,7,247,30,67,253,143,20,2,158,52,16,184,152,54,152,4,220,129,215,4,2,95,3,17, +203,146,198,80,106,12,147,85,234,8,149,158,66,0,30,8,193,213,28,169,32,184,147,180,3,58,20,72,200,52,173,162, +204,48,135,18,132,208,60,50,110,3,184,3,65,98,106,31,213,30,254,171,147,168,2,132,3,130,209,1,48,41,30,6, +59,149,6,65,106,184,192,150,87,130,24,236,18,64,79,70,78,5,134,138,123,8,9,7,3,144,162,228,38,4,84,178, +25,50,58,114,106,160,150,204,97,68,180,142,24,68,18,97,99,60,67,56,218,0,50,7,177,81,252,176,62,104,182,2, +83,108,160,103,33,97,181,69,2,2,88,135,42,6,193,157,112,129,156,175,8,195,196,65,81,189,10,78,5,210,3,165, +9,132,8,32,133,5,42,56,18,58,145,58,192,46,33,196,161,96,51,14,160,60,106,110,2,48,3,64,255,241,100,135, +188,94,75,147,91,152,3,144,208,5,30,45,186,10,44,66,180,172,176,6,103,9,224,93,193,187,141,147,2,153,124,27, +70,157,3,7,9,136,66,8,131,46,54,14,155,42,146,58,197,70,5,176,163,68,59,126,6,225,198,128,64,110,96,12, +133,1,128,251,194,9,2,203,223,73,13,79,117,30,98,208,131,109,186,26,222,4,6,5,147,146,27,193,82,88,136,241, +178,96,101,65,37,154,108,3,8,250,7,9,22,96,230,14,85,219,166,201,58,115,72,229,38,113,61,138,68,198,129,32, +12,165,228,215,0,92,15,220,2,144,119,129,0,211,25,250,30,5,69,9,3,69,117,83,60,203,149,186,6,5,20,103, +7,121,25,198,61,201,67,164,57,192,216,162,8,207,7,81,221,40,131,0,135,64,203,25,185,58,150,81,128,169,69,114, +18,22,4,208,22,15,107,43,130,106,0,184,31,164,15,184,32,141,3,64,207,161,5,119,8,240,42,116,234,21,202,18, +229,6,91,170,27,209,139,19,50,3,102,135,88,22,92,152,189,7,20,8,111,2,6,14,35,221,0,170,6,57,78,58, +162,230,164,67,151,160,48,29,50,205,6,165,214,0,88,15,202,20,31,112,64,77,42,70,125,5,2,66,3,192,161,2, +58,167,69,165,6,189,76,245,132,6,24,55,160,185,83,28,240,114,165,210,7,193,71,28,254,10,36,199,178,132,224,76, +18,194,240,16,224,88,27,137,36,208,25,240,5,66,1,45,144,138,143,58,184,122,151,210,8,233,5,64,240,32,72,29, +17,212,53,208,6,5,155,180,13,233,156,17,236,108,208,52,144,36,130,240,40,2,216,208,212,199,141,84,3,152,106,12, +161,49,231,160,58,102,38,241,46,80,3,128,118,35,201,79,104,64,62,200,2,143,145,6,15,176,107,154,21,229,234,6, +65,216,9,185,245,45,83,42,217,210,85,161,102,56,180,4,250,14,14,88,150,146,139,248,57,138,14,216,49,198,251,18, +14,8,203,187,16,7,0,234,223,32,250,30,209,129,250,106,222,32,161,6,34,29,32,107,138,210,123,168,223,176,81,178, +38,238,233,113,200,46,209,129,40,142,80,10,96,162,152,67,13,120,70,46,177,60,102,141,248,118,202,58,201,6,237,18, +84,48,20,224,3,128,125,24,15,92,252,24,60,93,231,228,244,53,145,6,74,34,87,30,9,115,101,212,9,123,65,56, +93,2,32,164,79,65,146,42,132,129,152,32,53,226,108,41,183,68,127,172,204,161,24,236,138,38,100,100,42,26,203,18, +28,54,102,108,169,0,104,36,2,7,221,60,177,4,24,15,161,5,75,88,6,209,73,98,17,30,238,203,33,175,26,230, +118,5,64,193,118,164,5,224,55,80,155,137,20,5,58,64,162,105,4,128,79,229,55,138,152,7,42,58,202,130,44,135, +18,141,142,105,5,240,1,128,136,2,7,156,208,60,46,246,130,56,169,106,18,34,42,30,139,58,146,125,200,193,175,92, +70,42,23,113,246,117,136,130,241,28,96,155,141,98,11,169,44,33,205,25,48,28,220,4,5,214,221,58,219,150,26,216, +18,120,139,184,20,209,125,0,80,15,172,2,65,3,30,194,60,131,223,58,26,227,196,212,96,219,17,116,30,22,236,6, +236,168,216,44,56,147,220,100,195,86,160,33,232,10,28,116,193,0,116,66,32,105,181,20,190,30,7,34,29,216,214,131, +131,118,59,57,126,0,76,33,1,110,200,52,61,177,60,247,13,114,77,251,81,1,52,151,30,66,165,177,226,64,181,195, +96,208,254,15,184,99,4,246,29,40,226,133,64,29,140,102,188,144,236,99,128,119,67,116,147,7,213,101,26,225,10,8, +2,179,16,56,160,2,64,116,143,206,66,108,24,194,66,58,63,178,89,16,175,106,18,50,2,93,30,10,139,200,122,245, +219,145,50,17,131,234,35,19,70,250,168,71,32,54,192,254,194,159,25,9,12,228,14,86,7,84,166,102,199,48,70,6, +123,56,121,2,246,0,128,31,99,172,96,161,1,163,60,23,210,195,92,119,39,236,64,219,17,84,30,74,58,213,26,203, +3,103,12,96,57,1,14,68,104,19,234,195,76,103,149,161,217,70,3,177,49,196,216,43,115,194,205,6,91,164,196,109, +22,237,76,80,40,26,176,171,64,62,133,18,168,181,150,3,10,16,31,10,163,249,37,13,121,98,36,143,105,38,30,193, +203,74,58,36,206,196,38,130,230,10,24,146,28,97,106,62,229,130,118,76,202,134,13,178,1,89,199,110,44,69,106,84, +22,226,198,117,1,178,64,205,117,192,61,132,18,123,209,239,144,119,103,30,5,249,37,9,129,233,96,227,36,18,219,30, +7,50,58,134,133,252,240,150,68,1,55,100,9,38,108,109,70,164,54,134,84,64,176,75,89,198,172,166,80,18,179,82, +40,48,164,214,9,160,96,219,0,68,43,18,159,180,135,210,161,214,32,60,172,95,10,146,152,9,90,99,6,216,157,40, +30,43,232,58,104,27,128,217,130,66,186,105,135,26,41,225,156,90,146,139,18,15,1,6,21,33,158,113,74,145,20,80, +64,136,77,169,2,130,99,110,202,5,129,90,1,240,47,1,225,133,58,206,128,242,224,254,9,44,15,97,21,1,50,18, +202,30,15,214,58,60,56,5,199,248,11,12,39,197,2,161,10,120,71,44,17,46,18,10,186,6,99,80,58,178,152,33, +38,160,53,104,20,193,149,36,208,18,5,100,171,128,123,7,223,227,1,58,4,230,15,160,127,2,76,146,192,247,159,18, +11,134,178,30,199,126,38,58,60,56,21,218,58,98,100,130,89,124,46,224,209,197,112,235,140,64,221,10,134,133,33,152, +34,135,97,53,228,130,196,162,56,130,57,158,45,245,92,113,0,24,13,2,58,16,4,128,244,133,250,71,22,205,153,170, +15,169,210,12,15,144,111,5,132,60,102,207,58,40,78,33,68,90,3,133,214,85,144,28,243,69,70,65,113,8,179,84, +168,147,51,101,18,147,35,81,45,27,117,74,38,124,196,233,92,18,128,42,7,208,246,67,4,1,115,38,221,11,232,74, +137,229,2,7,168,129,90,8,240,215,1,58,8,215,92,82,68,5,226,131,248,63,161,30,56,38,130,106,15,109,20,219, +210,6,212,81,140,198,232,42,97,88,84,9,196,148,212,83,0,11,129,242,73,7,64,24,20,143,219,118,47,81,162,80, +22,137,142,32,43,66,121,31,84,58,35,87,43,12,9,242,13,240,101,137,1,193,180,40,12,113,162,102,194,113,40,26, +242,96,199,77,98,128,75,136,205,76,249,110,214,18,152,2,64,124,18,33,4,12,15,104,145,59,162,89,219,166,36,143, +51,20,32,9,96,192,157,58,12,200,2,82,203,4,246,128,169,134,64,80,97,161,2,12,2,126,27,17,26,49,211,5, +151,80,67,148,9,102,107,98,18,128,0,34,7,87,1,23,92,4,32,23,192,210,59,162,67,3,250,52,9,195,1,119, +131,189,132,97,116,19,168,135,96,98,73,226,48,117,166,24,109,70,121,150,35,9,17,45,13,139,81,49,28,178,194,48, +100,9,0,176,10,114,224,31,192,0,1 +}; + +// Generated via: E:\code\demo\avrdemo\demo1\data\meshpack.exe -m 4095 cockpit.obj + +const unsigned short data_cockpit_obj::pal[5] PROGMEM_FAR = +{ +34,23507,51200,65535,4096 +}; + +const unsigned char data_cockpit_obj::data[416] PROGMEM_FAR = +{ +0,36,8,4,0,0,0,0,4,0,64,4,8,38,134,162,4,4,32,38,6,2,8,198,68,102,0,66,1,8,64,2, +16,129,3,208,130,4,26,12,11,244,6,148,167,8,5,91,7,2,85,79,39,9,1,35,167,3,34,126,165,37,55,181, +36,25,170,205,101,94,191,174,38,247,15,12,116,186,137,107,11,5,95,40,44,170,74,175,17,223,27,28,106,19,13,106, +25,31,85,24,170,23,187,100,210,173,8,3,185,15,172,1,225,253,26,106,29,87,32,182,7,77,28,222,20,164,239,6, +4,118,201,77,17,9,227,183,77,27,93,25,44,253,147,149,234,15,174,222,35,124,84,34,244,40,2,10,5,210,170,12, +189,0,116,210,245,8,39,7,22,213,110,230,14,173,33,100,233,18,35,16,233,21,25,20,41,30,124,177,43,31,18,174, +223,95,32,230,2,38,189,39,5,7,13,147,197,222,161,30,122,181,24,210,76,211,153,14,63,1,4,37,6,3,191,36, +121,38,103,9,163,18,16,73,91,33,254,17,28,31,133,30,138,11,42,199,29,15,65,234,136,168,249,7,69,188,195,166, +235,245,191,248,188,128,19,84,228,21,240,60,252,220,29,107,172,228,234,120,30,142,176,227,36,226,26,177,229,238,76,210, +56,113,33,203,143,222,111,158,51,203,60,18,46,129,243,145,204,102,23,150,140,100,96,150,212,179,115,64,68,51,124,160, +231,72,18,230,108,27,72,107,184,141,237,70,88,138,240,234,123,92,192,48,119,85,146,187,61,174,64,207,50,28,24,41, +56,23,104,230,199,243,3,35,159,56,140,60,15,136,140,225,139,9,99,196,240,83,152,82,214,203,174,41,28,24,252,58, +189,211,40,70,228,101,186,141,27,113,183,193,198,73,62,117,207,252,173,68,101,83,249,187,182,80,155,242,221,0,0,64 +}; + +// Generated via: E:\code\demo\avrdemo\demo1\data\meshpack.exe -m 4095 cube.obj + +const unsigned short data_cube_obj::pal[3] PROGMEM_FAR = +{ +41088,40960,41440 +}; + +const unsigned char data_cube_obj::data[70] PROGMEM_FAR = +{ +14,0,4,0,4,1,4,2,94,0,1,2,3,4,5,6,3,5,7,0,2,0,7,5,6,2,1,3,6,1,4,0, +5,4,3,1,7,2,6,134,37,71,5,7,6,0,1,0,255,133,3,42,66,0,255,200,11,136,13,133,4,8,136,18, +133,1,13,66,0,1 +}; + +// Generated via: E:\code\demo\avrdemo\demo1\data\meshpack.exe -m 4095 icosahedron.obj + +const unsigned char data_icosahedron_obj::data[109] PROGMEM_FAR = +{ +2,2,70,0,0,0,0,0,0,0,0,0,4,64,2,160,0,0,0,0,0,2,40,160,98,38,0,1,12,2,1,139, +3,10,244,8,6,7,11,165,12,90,171,9,181,4,30,5,1,153,219,246,189,126,249,79,214,234,2,183,175,29,181,76, +123,6,83,215,118,8,177,219,215,139,76,167,205,99,204,194,178,189,82,131,19,115,1,124,62,77,108,76,164,76,164,111, +251,124,33,55,48,23,31,27,15,197,192,0,16 +}; + +// Generated via: E:\code\demo\avrdemo\demo1\data\meshpack.exe -m 4095 -q 0.75 revisionflat.obj + +const unsigned short data_revisionflat_obj::pal[1] PROGMEM_FAR = +{ +52857 +}; + +const unsigned char data_revisionflat_obj::data[1024] PROGMEM_FAR = +{ +0,0,2,70,138,192,0,0,130,130,130,194,204,76,44,33,32,32,6,161,192,160,140,224,32,4,0,0,8,7,1,2, +3,223,4,5,101,254,46,36,37,105,106,107,0,253,7,83,18,239,55,27,54,255,8,6,10,11,9,116,114,190,115,243, +169,222,12,11,220,117,118,187,14,13,93,15,16,136,167,17,18,98,41,170,19,127,0,20,21,22,23,24,163,25,26,27, +234,32,33,34,159,35,38,39,40,234,41,42,43,153,44,69,178,38,181,45,207,52,29,51,96,239,95,94,37,251,45,8, +9,6,95,46,218,35,36,32,196,83,47,178,31,216,30,138,118,48,49,34,157,50,51,136,167,52,66,238,65,20,29,85, +53,59,55,54,45,27,52,56,92,25,171,23,170,157,57,22,136,167,58,20,101,232,41,59,60,221,61,62,101,178,42,217, +63,108,64,189,109,111,112,199,182,65,66,95,69,96,98,6,255,7,5,58,66,20,67,68,69,253,72,73,84,74,75,76, +83,77,246,78,79,80,81,87,152,138,118,82,74,34,153,83,100,175,14,13,11,117,253,85,72,150,71,203,70,98,41,170, +86,118,87,88,86,185,89,90,75,42,118,91,92,34,154,93,166,94,139,95,254,130,127,129,96,253,97,83,98,167,69,99, +67,101,109,100,101,147,102,177,67,20,243,130,103,104,0,234,78,81,157,107,108,245,247,121,116,119,103,95,69,109,202,110, +90,113,159,112,123,121,120,149,110,22,114,34,157,115,119,57,118,116,46,120,121,191,54,28,29,122,123,126,0,102,124,235, +232,221,125,126,101,177,127,20,213,128,58,129,130,118,131,67,95,167,236,13,248,55,152,228,222,223,242,252,160,226,135,234, +157,191,175,134,147,242,240,249,183,220,126,15,253,231,235,79,116,1,201,58,247,241,128,250,74,69,242,183,11,12,240,126, +58,7,52,251,79,209,18,136,72,47,74,8,39,147,2,242,248,17,129,6,126,118,3,149,8,127,65,13,164,243,255,168, +3,131,133,19,121,236,7,63,61,244,40,17,39,254,216,245,27,159,22,235,116,148,3,242,131,49,44,15,126,180,39,196, +12,79,78,40,139,218,47,211,185,36,237,39,83,134,27,17,242,239,18,49,232,126,14,10,233,227,78,169,246,67,217,79, +112,253,201,165,213,249,202,252,31,204,62,71,205,111,39,242,202,213,74,245,119,98,114,139,1,123,12,226,206,134,71,201, +6,9,249,249,215,52,14,63,44,224,200,28,39,203,228,218,185,36,252,251,227,97,44,159,171,232,127,147,35,242,190,242, +26,40,126,134,2,24,42,79,45,20,201,210,57,249,178,255,245,60,60,54,235,33,126,105,212,97,43,79,246,197,198,149, +20,23,134,194,53,43,49,162,235,150,120,216,102,222,74,233,214,39,132,225,230,204,210,75,194,101,198,194,228,66,195,252, +123,9,25,214,44,184,71,251,132,42,248,109,57,87,145,134,60,119,240,181,1,78,28,158,186,33,158,20,19,212,21,79, +157,27,194,190,29,139,96,137,43,46,36,231,35,240,199,44,76,26,172,47,242,38,207,68,185,126,133,212,171,193,55,31, +198,228,204,22,239,219,44,147,79,242,43,223,192,68,126,117,242,123,73,79,31,255,136,49,47,5,26,30,147,83,241,37, +22,193,71,142,249,35,73,66,63,167,41,163,75,25,174,64,124,123,52,126,90,79,94,49,201,163,86,249,141,57,166,64, +63,234,32,213,94,39,175,228,4,110,100,248,69,255,193,146,249,115,232,160,97,62,93,211,21,40,190,194,192,64,78,45, +27,217,25,61,63,60,177,188,62,39,56,198,179,237,239,227,103,165,34,159,92,169,207,10,39,219,228,157,14,23,252,29, +155,67,246,159,95,179,159,31,107,223,96,182,138,232,95,163,107,216,24,239,174,215,147,196,242,193,185,73,184,123,2,182, +145,180,218,121,193,131,171,63,200,196,244,174,39,127,199,99,128,252,119,11,105,34,4,158,189,102,152,28,233,120,90,46, +247,7,74,139,188,83,240,198,83,127,191,3,86,162,255,39,245,228,96,40,233,248,213,76,210,133,249,167,71,62,234,63, +26,82,69,229,39,106,199,89,20,187,121,71,242,208,124,148,77,123,110,63,152,63,99,214,12,227,224,147,202,242,48,60, +249,194,124,230,49,251,115,14,228,62,246,177,248,30,28,142,191,115,43,198,34,166,107,228,22,151,158,252,6,25,118,184, +62,215,24,143,133,248,199,202,155,241,245,244,243,11,185,249,164,147,170,242,182,215,155,163,126,63,219,1,178,76,0,1 +}; + +// Generated via: E:\code\demo\avrdemo\demo1\data\meshpack.exe -m 4095 -q 3 sofa.obj + +const unsigned short data_sofa_obj::pal[6] PROGMEM_FAR = +{ +25281,46338,10900,22752,35961,4426 +}; + +const unsigned char data_sofa_obj::data[583] PROGMEM_FAR = +{ +2,2,96,64,64,0,0,0,4,0,66,66,170,40,44,14,32,32,2,32,6,96,194,98,32,70,0,2,16,1,112,65, +2,225,3,1,194,4,0,66,5,11,14,16,1,3,169,239,17,12,7,213,62,13,18,6,4,169,223,19,14,213,1,55, +15,119,12,234,151,247,7,174,85,62,5,0,16,17,169,47,234,18,19,151,119,13,213,10,78,11,4,218,161,170,9,167, +8,16,144,0,165,187,1,107,19,190,4,84,191,124,218,222,15,191,84,237,10,5,175,77,31,32,93,76,45,82,223,49, +53,60,84,250,59,72,73,57,166,58,241,195,106,151,125,21,44,76,83,77,103,30,29,84,146,220,85,32,62,25,20,23, +34,169,179,33,38,170,36,73,53,35,37,75,238,39,40,43,169,219,42,41,33,79,24,48,61,170,62,104,51,90,64,166, +66,237,237,65,83,63,234,67,70,71,158,68,69,52,218,165,223,26,24,32,93,22,27,83,28,112,218,245,25,23,20,77, +21,203,76,170,75,56,118,92,86,46,169,254,47,54,60,52,50,55,27,169,185,22,182,56,169,118,245,166,122,53,49,48, +166,51,230,60,202,165,167,119,72,157,74,75,47,83,46,180,58,202,165,231,22,26,32,84,213,31,53,45,79,29,50,59, +170,60,123,54,75,57,149,77,47,250,182,169,124,133,255,59,74,249,65,3,185,1,25,251,248,167,6,119,254,182,191,184, +2,204,0,201,247,188,30,233,139,253,177,42,87,31,157,169,252,216,229,75,229,97,37,75,51,255,188,89,188,79,177,94, +235,253,249,24,4,101,1,37,171,130,206,63,255,71,250,155,87,254,181,251,26,246,4,42,0,157,173,74,118,188,253,227, +173,239,127,158,254,19,250,27,6,21,121,119,106,159,213,42,231,235,248,13,246,5,65,0,155,191,132,4,85,255,36,251, +237,215,2,232,90,117,250,96,3,212,0,248,21,28,120,5,28,141,130,50,126,208,7,224,95,139,245,132,178,136,250,114, +96,171,164,6,198,168,253,20,46,39,35,39,203,32,3,11,18,14,80,86,113,99,43,128,204,56,255,149,80,137,246,10, +178,176,4,113,41,240,224,158,224,116,13,204,74,251,196,133,67,130,178,114,216,101,76,150,4,106,36,55,67,34,28,17, +227,8,7,214,228,135,137,199,177,40,10,226,49,226,113,226,113,225,133,11,135,137,199,132,134,80,60,84,130,231,131,216, +176,9,241,56,241,56,245,16,224,244,32,232,46,30,43,30,21,14,7,139,97,144,240,144,221,15,19,143,15,151,199,112, +255,245,218,212,0,0,64 +}; + +const uint8_t data_raw_guru[98+1] PROGMEM_FAR = { +72, 97, 114, 100, 119, 97, 114, 101, 32, 70, 97, 105, 108, 117, 114, 101, 46, 32, 32, 32, 80, 114, 101, 115, 115, 32, 115, 101, 108, 102, 45, 100, 101, 115, 116, 114, 117, 99, 116, 32, 116, 111, 32, 99, 111, 110, 116, 105, 110, 117, 101, 46, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 71, 117, 114, 117, 32, 77, 101, 100, 105, 116, 97, 116, 105, 111, 110, 32, 35, 48, 48, 48, 48, 48, 48, 48, 57, 46, 66, 48, 48, 50, 69, 52, 50, 48, 10, 0 +}; +const uint8_t data_raw_intro1[29+1] PROGMEM_FAR = { +77, 101, 97, 110, 119, 104, 105, 108, 101, 32, 105, 110, 32, 83, 97, 97, 114, 98, 114, 111, 111, 107, 108, 121, 110, 46, 46, 46, 10, 0 +}; diff --git a/democode/data.gen.h b/democode/data.gen.h new file mode 100644 index 0000000..3c6b16f --- /dev/null +++ b/democode/data.gen.h @@ -0,0 +1,341 @@ +#pragma once +#include "data.def.h" + +extern const uint8_t data_exo_a2560_path[120] PROGMEM_FAR; +extern const uint8_t data_exo_a2560_title[131] PROGMEM_FAR; +extern const uint8_t data_exo_cbm1[961] PROGMEM_FAR; +extern const uint8_t data_exo_cbm2[942] PROGMEM_FAR; +extern const uint8_t data_exo_credits[120] PROGMEM_FAR; +extern const uint8_t data_exo_greets[437] PROGMEM_FAR; +extern const uint8_t data_exo_hwmf[581] PROGMEM_FAR; +extern const uint8_t data_exo_hwmf_cross[49] PROGMEM_FAR; +extern const uint8_t data_exo_hwmf_cube[61] PROGMEM_FAR; +extern const uint8_t data_exo_hwmf_nameoverlay[122] PROGMEM_FAR; +extern const uint8_t data_exo_hwmf_path_cross[45] PROGMEM_FAR; +extern const uint8_t data_exo_hwmf_path_cube[44] PROGMEM_FAR; +extern const uint8_t data_exo_hwmf_path_rmcc[61] PROGMEM_FAR; +extern const uint8_t data_exo_intro1[56] PROGMEM_FAR; +extern const uint8_t data_exo_intro2[72] PROGMEM_FAR; +extern const uint8_t data_exo_paths[422] PROGMEM_FAR; +extern const uint8_t data_exo_specs[317] PROGMEM_FAR; +extern const uint8_t data_exo_theend[138] PROGMEM_FAR; +struct data_3x5_glf +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\glfconvert.exe 3x5.glf greets.txt + +static const unsigned paloffs = 0; +static const bool blocked = false; +static const PackType packtype = PK_EXO; +static const unsigned windowsize = 291; +static const unsigned ndata = 213; +static const unsigned char data[213] PROGMEM_FAR; +static const unsigned packedsize = 213; +static const unsigned fullsize = 482; +static const unsigned nusedch = 58; +static const uint8_t usedch[58] PROGMEM_FAR; +static const unsigned noffsets = 58; +static const uint16_t offsets[58] PROGMEM_FAR; +static const uint8_t fontheight = 7; +}; +struct data_topaz_glf +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\glfconvert.exe topaz.glf guru.txt + +static const unsigned paloffs = 0; +static const bool blocked = false; +static const PackType packtype = PK_EXO; +static const unsigned windowsize = 551; +static const unsigned ndata = 268; +static const unsigned char data[268] PROGMEM_FAR; +static const unsigned packedsize = 268; +static const unsigned fullsize = 584; +static const unsigned nusedch = 29; +static const uint8_t usedch[29] PROGMEM_FAR; +static const unsigned noffsets = 29; +static const uint16_t offsets[29] PROGMEM_FAR; +static const uint8_t fontheight = 18; +}; +struct data_vga_glf +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\glfconvert.exe vga.glf intro1.txt intro2.txt credits.txt theend.txt + +static const unsigned paloffs = 0; +static const bool blocked = false; +static const PackType packtype = PK_EXO; +static const unsigned windowsize = 636; +static const unsigned ndata = 368; +static const unsigned char data[368] PROGMEM_FAR; +static const unsigned packedsize = 368; +static const unsigned fullsize = 769; +static const unsigned nusedch = 46; +static const uint8_t usedch[46] PROGMEM_FAR; +static const unsigned noffsets = 46; +static const uint16_t offsets[46] PROGMEM_FAR; +static const uint8_t fontheight = 18; +}; +struct data_wire_wolf_gif +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe -p exo wire_wolf.gif + +static const unsigned paloffs = 0; +static const bool blocked = false; +static const uint32_t fullsize = 100160; +static const unsigned w = 313; +static const unsigned h = 320; +static const unsigned npal = 115; +static const unsigned short pal[115] PROGMEM_FAR; +static const PackType packtype = PK_EXO; +static const unsigned windowsize = 255; +static const unsigned ndata = 27211; +static const unsigned char data[27211] PROGMEM_FAR; +static const unsigned packedsize = 27211; +static const bool useRLERecomp = false; +}; +struct data_cityscroll1_gif +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe -b 1 0 -s 5 cityscroll1.gif + +static const unsigned paloffs = 0; +static const bool blocked = true; +static const uint32_t fullsize = 253760; +static const unsigned w = 793; +static const unsigned h = 320; +static const unsigned npal = 16; +static const unsigned short pal[16] PROGMEM_FAR; +static const unsigned blockw = 1; +static const unsigned blockh = 320; +static const PackType packtype = PK_BLOCK_TSCHUNK; +static const unsigned windowsize = 0; +static const unsigned ndata = 25359; +static const unsigned char data[25359] PROGMEM_FAR; +static const unsigned packedsize = 25359; +static const bool useRLERecomp = false; +}; +struct data_cityscroll2_gif +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe -b 1 0 -s 5 cityscroll2.gif + +static const unsigned paloffs = 0; +static const bool blocked = true; +static const uint32_t fullsize = 258240; +static const unsigned w = 807; +static const unsigned h = 320; +static const unsigned npal = 19; +static const unsigned short pal[19] PROGMEM_FAR; +static const unsigned blockw = 1; +static const unsigned blockh = 320; +static const PackType packtype = PK_BLOCK_TSCHUNK; +static const unsigned windowsize = 0; +static const unsigned ndata = 19853; +static const unsigned char data[19853] PROGMEM_FAR; +static const unsigned packedsize = 19853; +static const bool useRLERecomp = false; +}; +struct data_github_png +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe github.png + +static const unsigned paloffs = 0; +static const bool blocked = false; +static const unsigned fullsize = 1024; +static const unsigned w = 32; +static const unsigned h = 32; +static const unsigned npal = 2; +static const unsigned short pal[2] PROGMEM_FAR; +static const PackType packtype = PK_EXO; +static const unsigned windowsize = 241; +static const unsigned ndata = 109; +static const unsigned char data[109] PROGMEM_FAR; +static const unsigned packedsize = 109; +static const bool useRLERecomp = false; +}; +struct data_mdscroll_png +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe -b 0 1 -s 5 -p exo mdscroll.png + +static const unsigned paloffs = 0; +static const bool blocked = true; +static const unsigned fullsize = 53120; +static const unsigned w = 166; +static const unsigned h = 320; +static const unsigned npal = 10; +static const unsigned short pal[10] PROGMEM_FAR; +static const unsigned blockw = 166; +static const unsigned blockh = 1; +static const PackType packtype = PK_BLOCK_EXO; +static const unsigned windowsize = 129; +static const unsigned ndata = 16520; +static const unsigned char data[16520] PROGMEM_FAR; +static const unsigned packedsize = 16520; +static const bool useRLERecomp = false; +}; +struct data_pcb64_png +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe pcb64.png + +static const unsigned paloffs = 0; +static const bool blocked = false; +static const unsigned fullsize = 4096; +static const unsigned w = 64; +static const unsigned h = 64; +static const unsigned npal = 14; +static const unsigned short pal[14] PROGMEM_FAR; +static const PackType packtype = PK_EXO; +static const unsigned windowsize = 255; +static const unsigned ndata = 665; +static const unsigned char data[665] PROGMEM_FAR; +static const unsigned packedsize = 665; +static const bool useRLERecomp = false; +}; +struct data_screenborder_png +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe screenborder.png + +static const unsigned paloffs = 0; +static const bool blocked = false; +static const uint32_t fullsize = 153600; +static const unsigned w = 480; +static const unsigned h = 320; +static const unsigned npal = 14; +static const unsigned short pal[14] PROGMEM_FAR; +static const PackType packtype = PK_TSCHUNK; +static const unsigned windowsize = 0; +static const unsigned ndata = 1790; +static const unsigned char data[1790] PROGMEM_FAR; +static const unsigned packedsize = 1790; +static const bool useRLERecomp = false; +}; +struct data_sideup_png +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe -p exo sideup.png + +static const unsigned paloffs = 0; +static const bool blocked = false; +static const unsigned fullsize = 8192; +static const unsigned w = 128; +static const unsigned h = 64; +static const unsigned npal = 2; +static const unsigned short pal[2] PROGMEM_FAR; +static const PackType packtype = PK_EXO; +static const unsigned windowsize = 253; +static const unsigned ndata = 280; +static const unsigned char data[280] PROGMEM_FAR; +static const unsigned packedsize = 280; +static const bool useRLERecomp = false; +}; +struct data_tiles_gif +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\imgpack.exe -b 32 1 -p tsc tiles.gif + +static const unsigned paloffs = 0; +static const bool blocked = true; +static const unsigned fullsize = 11264; +static const unsigned w = 32; +static const unsigned h = 352; +static const unsigned npal = 49; +static const unsigned short pal[49] PROGMEM_FAR; +static const unsigned blockw = 32; +static const unsigned blockh = 1; +static const PackType packtype = PK_BLOCK_TSCHUNK; +static const unsigned windowsize = 0; +static const unsigned ndata = 4535; +static const unsigned char data[4535] PROGMEM_FAR; +static const unsigned packedsize = 4535; +static const bool useRLERecomp = false; +}; +struct data_cockpit_obj +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\meshpack.exe -m 4095 cockpit.obj + +static constexpr inline fp1616 scale() { return fp1616::raw(1996809); } +static const unsigned Ntris = 78; +static const unsigned Nverts = 41; +static const bool Indexed = true; +static const unsigned paloffs = 0; +static const bool blocked = false; +static const unsigned npal = 5; +static const unsigned short pal[5] PROGMEM_FAR; +static const PackType packtype = PK_EXO; +static const unsigned windowsize = 124; +static const unsigned ndata = 416; +static const unsigned char data[416] PROGMEM_FAR; +static const unsigned packedsize = 416; +static const unsigned fullsize = 558; +}; +struct data_cube_obj +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\meshpack.exe -m 4095 cube.obj + +static constexpr inline fp1616 scale() { return fp1616::raw(65536); } +static const unsigned Ntris = 12; +static const unsigned Nverts = 8; +static const bool Indexed = true; +static const unsigned paloffs = 0; +static const bool blocked = false; +static const unsigned npal = 3; +static const unsigned short pal[3] PROGMEM_FAR; +static const PackType packtype = PK_TSCHUNK; +static const unsigned windowsize = 0; +static const unsigned ndata = 70; +static const unsigned char data[70] PROGMEM_FAR; +static const unsigned packedsize = 70; +static const unsigned fullsize = 96; +}; +struct data_icosahedron_obj +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\meshpack.exe -m 4095 icosahedron.obj + +static constexpr inline fp1616 scale() { return fp1616::raw(5062144); } +static const unsigned Ntris = 20; +static const unsigned Nverts = 12; +static const bool Indexed = true; +static const unsigned paloffs = 0; +static const bool blocked = false; +static const unsigned npal = 0; +static const PackType packtype = PK_EXO; +static const unsigned windowsize = 64; +static const unsigned ndata = 109; +static const unsigned char data[109] PROGMEM_FAR; +static const unsigned packedsize = 109; +static const unsigned fullsize = 152; +}; +struct data_revisionflat_obj +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\meshpack.exe -m 4095 -q 0.75 revisionflat.obj + +static constexpr inline fp1616 scale() { return fp1616::raw(1458435); } +static const unsigned Ntris = 132; +static const unsigned Nverts = 132; +static const bool Indexed = true; +static const unsigned paloffs = 0; +static const bool blocked = false; +static const unsigned npal = 1; +static const unsigned short pal[1] PROGMEM_FAR; +static const PackType packtype = PK_EXO; +static const unsigned windowsize = 630; +static const unsigned ndata = 1024; +static const unsigned char data[1024] PROGMEM_FAR; +static const unsigned packedsize = 1024; +static const unsigned fullsize = 1320; +}; +struct data_sofa_obj +{ +// Generated via: E:\code\demo\avrdemo\demo1\data\meshpack.exe -m 4095 -q 3 sofa.obj + +static constexpr inline fp1616 scale() { return fp1616::raw(170669); } +static const unsigned Ntris = 102; +static const unsigned Nverts = 78; +static const bool Indexed = true; +static const unsigned paloffs = 0; +static const bool blocked = false; +static const unsigned npal = 6; +static const unsigned short pal[6] PROGMEM_FAR; +static const PackType packtype = PK_EXO; +static const unsigned windowsize = 276; +static const unsigned ndata = 583; +static const unsigned char data[583] PROGMEM_FAR; +static const unsigned packedsize = 583; +static const unsigned fullsize = 876; +}; +extern const uint8_t data_raw_guru[98+1] PROGMEM_FAR; +extern const uint8_t data_raw_intro1[29+1] PROGMEM_FAR; diff --git a/democode/data.h b/democode/data.h new file mode 100644 index 0000000..1479052 --- /dev/null +++ b/democode/data.h @@ -0,0 +1,5 @@ +#pragma once + +#include "src/demolib/fastlookup.h" + +#include "data.gen.h" diff --git a/democode/demo.cpp b/democode/demo.cpp new file mode 100644 index 0000000..d64d567 --- /dev/null +++ b/democode/demo.cpp @@ -0,0 +1,106 @@ +#include "demo.h" +#include "demoshared.h" +#include "src/demolib/demodebug.h" + +#ifdef __GNUC__ +#pragma GCC optimize ("Os") +#endif + +static void _doNextPart(void*) +{ + partdone = true; +} + +void DoNextPartIn(unsigned ms) +{ + partdone = false; + evs::schedule(ms, _doNextPart, NULL); +} + +void WaitPartDone() +{ +#ifdef MCU_IS_PC + PalBackup bk; + fuckpal(); // Visual indicator that we're waiting +#endif + while(!partdone) + { +#ifdef MCU_IS_PC + SDL_Delay(1); +#endif + } + partdone = false; +} + +void SyncChannel0Callback(uint8_t channel, pmf_channel_info info, void *ud) +{ + //printf("ch[%c]: note:%u, vol:%u, eff:%u, effd:%u\n", + // info.note_hit ? 'X' : ' ', info.base_note, info.volume, info.effect, info.effect_data); + + // TODO check note + partdone = true; +} + +void cls() +{ + LCD::clear(0); +} + +static const uint8_t s_music[] PROGMEM = { +#include "music.gen.hh" +}; + +void demoinit() +{ + evs::init(); + demo_init_debug(); + + LCD::init(); + LCD::enableCS(); + LCD::enableDisplay(); + + fglcd::delay_ms(100); // Wait until shit has settled + + // If this ever triggers, check scratchbuffer alignment in src/demolib/globals.cpp + u16 err = 0; + if(uintptr_t(scratch0) & 0xff) + err |= LCD::gencolor_inl(0xff,0,0); + else if(uintptr_t(&G.mus.audiobuf.buffer[0]) & 0xff) + err |= LCD::gencolor_inl(0,0xff,0); + if(err) + { + LCD::clear(err); + for(;;) {} + } + + cls(); + part_thissideup(); + + music_init(&s_music[0]); + cls(); +} + +void demomain() +{ + musync::SetChannelCallback(0, SyncChannel0Callback, NULL, musync::CallOnHit); + music_play(true); + + part_intro1(); evs::killAllEvents(); + cls(); + part_widescroll(); evs::killAllEvents(); + cls(); + part_intro2(); evs::killAllEvents(); + part_c64(); evs::killAllEvents(); + part_c64_to_cube(); evs::killAllEvents(); + cls(); + part_twist(); evs::killAllEvents(); + cls(); + part_wolf(); evs::killAllEvents(); + part_megadrivescroll(); evs::killAllEvents(); + part_test_landscape(); evs::killAllEvents(); + cls(); + part_greet(); evs::killAllEvents(); + part_gol(); evs::killAllEvents(); + cls(); + part_end(); +} diff --git a/democode/demo.h b/democode/demo.h new file mode 100644 index 0000000..17e6b0b --- /dev/null +++ b/democode/demo.h @@ -0,0 +1,4 @@ +#pragma once + +void demoinit(); +void demomain(); diff --git a/democode/democode.ino b/democode/democode.ino new file mode 100644 index 0000000..3694bb5 --- /dev/null +++ b/democode/democode.ino @@ -0,0 +1,30 @@ +// Warning! +// Arduino usually ships a very old and buggy avr-gcc version! +// Before you try to compile this, make sure you're using avr-gcc 9.2 or better! +// Upgrade instructions + binaries here: https://blog.zakkemble.net/avr-gcc-builds/ + +#include +#include "demo.h" + +// Stub out some libcxx shit that's never supposed to be called anyway +extern void __cxa_pure_virtual() { while(1); } +namespace __gnu_cxx { void __verbose_terminate_handler() { while(1); }} + +int main() +{ + //wdt_disable(); + sei(); + demoinit(); + + for(;;) + demomain(); +} + +// Note to self: Enable printing floats properly: +// Find in Arduino/hardware/arduino/avr/platform.txt: +// -lm (should occur only once at the end of a line) +// replace with: +// -Wl,-u,vfprintf -lprintf_flt -lm +// Makes all compiles that use printf() and friends much bigger but +// is the only sane way to printf() floats for debugging. +// This is NOT required for the demo to function! diff --git a/democode/demoshared.h b/democode/demoshared.h new file mode 100644 index 0000000..46891b3 --- /dev/null +++ b/democode/demoshared.h @@ -0,0 +1,93 @@ +// Included by all parts + +#pragma once + +#include +#include +#include + +#include "src/demolib/demo_def.h" +#include "src/demolib/raster.h" +#include "src/demolib/vertex.h" +#include "src/demolib/drawmesh.h" +#include "src/demomath/tinyrng.h" +#include "src/demomath/fgmath.h" +#include "src/demolib/eventsystem.h" +#include "src/demolib/draw.h" +#include "src/demolib/drawhelper.h" +#include "src/demolib/decomp_lcd.h" +#include "src/demomath/camera.h" +#include "src/demolib/scrollhelper.h" +#include "src/demolib/drawfont.h" +#include "src/demolib/farload.h" +#include "src/demolib/interpolator.h" +#include "src/demolib/music.h" +#include "src/demolib/debugthing.h" +#include "src/demolib/demodebug.h" +#include "src/demomath/fast_hsv2rgb.h" +#include "src/demolib/musicsyncvar.h" + +#include "data.h" + + +using fglcd::types::u8; +using fglcd::types::u16; +using fglcd::types::u32; +using fglcd::types::s8; +using fglcd::types::s16; +using fglcd::types::s32; + +#define partdone G.demo.partdone + +void DoNextPartIn(unsigned ms); +void WaitPartDone(); +void cls(); + +//demopart partmissing(const char *name_P, uint16_t ms, bool wait); +demopart part_strobo(); +demopart part_c64(); +demopart part_layerchess(); +demopart test_rotozoom(); +demopart part_gol(); +demopart part_widescroll(); +demopart part_twist(); +demopart test_showimage(); +demopart test_showwaveform(); +demopart part_sierpscroll(); +demopart part_c64_to_cube(); +demopart part_test_3dobj(); +demopart part_greet(); +demopart part_test_landscape(); +demopart part_wolf(); +demopart part_megadrivescroll(); +demopart part_thissideup(); +demopart part_cityscroll(); +demopart part_intro1(); +demopart part_intro2(); +demopart part_end(); + +#define BEATSYNC_CHANNEL1 1 +#define BEATSYNC_CHANNEL2 2 +#define BEATSYNC_CHANNEL3 3 + +#ifdef CFG_USE_DEBUG_THING +#define DEBUG_THING(f, ud) DebugThing _dbg((f), (ud)) +#define DEBUG_RETURN_IF_DONE() do { if(partdone) return; } while(0) +#else +#define DEBUG_THING(f, ud) +#define DEBUG_RETURN_IF_DONE() do {} while(0) +#endif + +static FORCEINLINE Color gencolor(const u8vec3& v) +{ + return LCD::gencolor(v.x, v.y, v.z); +} + +static inline Color gencolorScaled(const u8vec3& v, u8 scale) +{ + return LCD::gencolor( + scale8(v.r, scale), + scale8(v.g, scale), + scale8(v.b, scale) + ); +} diff --git a/democode/gol.cpp b/democode/gol.cpp new file mode 100644 index 0000000..83d2f39 --- /dev/null +++ b/democode/gol.cpp @@ -0,0 +1,596 @@ +#include "demoshared.h" + +#ifdef __GNUC__ +#pragma GCC optimize ("Os") +#endif + +#define GREETINGS_SIZE 600 // gotta be enough + +#define DELAY_MS(x) fglcd::delay_ms(x) + +static const u8 BORDER = 1; +static const u8 YSTART = 1; +static const u8 XSTART = 1; + +static const u8 BLOCKSIZE = 4; +static const u8 NPIX = BLOCKSIZE * BLOCKSIZE; + +const u16 WR = u16(LCD::WIDTH - XSTART - BORDER) / u16(BLOCKSIZE+BORDER); +const u16 W = WR / 4; +const u16 H = u16(LCD::HEIGHT - YSTART - BORDER) / u16(BLOCKSIZE+BORDER); + +const u16 NX = 2 + W; +const u16 NY = 2 + H; +const u16 NBUF = NX * H; + + +// compresses bits [aabbccdd] into [0000abcd] (00 -> 0, otherwise 1) +static const u8 compressbits[] PROGMEM_FAR = { // copied to scratch2 +0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 4, 5, 5, 5, 6, 7, 7, 7, 6, 7, 7, 7, 6, 7, 7, 7, 4, 5, 5, 5, 6, 7, 7, 7, 6, 7, 7, 7, 6, 7, 7, 7, 4, 5, 5, 5, 6, 7, 7, 7, 6, 7, 7, 7, 6, 7, 7, 7, 8, 9, 9, 9, 10, 11, 11, 11, 10, 11, 11, 11, 10, 11, 11, 11, 12, 13, 13, 13, 14, 15, 15, 15, 14, 15, 15, 15, 14, 15, 15, 15, 12, 13, 13, 13, 14, 15, 15, 15, 14, 15, 15, 15, 14, 15, 15, 15, 12, 13, 13, 13, 14, 15, 15, 15, 14, 15, 15, 15, 14, 15, 15, 15, 8, 9, 9, 9, 10, 11, 11, 11, 10, 11, 11, 11, 10, 11, 11, 11, 12, 13, 13, 13, 14, 15, 15, 15, 14, 15, 15, 15, 14, 15, 15, 15, 12, 13, 13, 13, 14, 15, 15, 15, 14, 15, 15, 15, 14, 15, 15, 15, 12, 13, 13, 13, 14, 15, 15, 15, 14, 15, 15, 15, 14, 15, 15, 15, 8, 9, 9, 9, 10, 11, 11, 11, 10, 11, 11, 11, 10, 11, 11, 11, 12, 13, 13, 13, 14, 15, 15, 15, 14, 15, 15, 15, 14, 15, 15, 15, 12, 13, 13, 13, 14, 15, 15, 15, 14, 15, 15, 15, 14, 15, 15, 15, 12, 13, 13, 13, 14, 15, 15, 15, 14, 15, 15, 15, 14, 15, 15, 15 +}; +// for each aa,bb,cc,dd compute (00 -> 01, 01 -> 10, 10 -> 11, 11 -> 11) +static const u8 advancebits[] PROGMEM_FAR = { // copied to scratch3 +85, 86, 87, 87, 89, 90, 91, 91, 93, 94, 95, 95, 93, 94, 95, 95, 101, 102, 103, 103, 105, 106, 107, 107, 109, 110, 111, 111, 109, 110, 111, 111, 117, 118, 119, 119, 121, 122, 123, 123, 125, 126, 127, 127, 125, 126, 127, 127, 117, 118, 119, 119, 121, 122, 123, 123, 125, 126, 127, 127, 125, 126, 127, 127, 149, 150, 151, 151, 153, 154, 155, 155, 157, 158, 159, 159, 157, 158, 159, 159, 165, 166, 167, 167, 169, 170, 171, 171, 173, 174, 175, 175, 173, 174, 175, 175, 181, 182, 183, 183, 185, 186, 187, 187, 189, 190, 191, 191, 189, 190, 191, 191, 181, 182, 183, 183, 185, 186, 187, 187, 189, 190, 191, 191, 189, 190, 191, 191, 213, 214, 215, 215, 217, 218, 219, 219, 221, 222, 223, 223, 221, 222, 223, 223, 229, 230, 231, 231, 233, 234, 235, 235, 237, 238, 239, 239, 237, 238, 239, 239, 245, 246, 247, 247, 249, 250, 251, 251, 253, 254, 255, 255, 253, 254, 255, 255, 245, 246, 247, 247, 249, 250, 251, 251, 253, 254, 255, 255, 253, 254, 255, 255, 213, 214, 215, 215, 217, 218, 219, 219, 221, 222, 223, 223, 221, 222, 223, 223, 229, 230, 231, 231, 233, 234, 235, 235, 237, 238, 239, 239, 237, 238, 239, 239, 245, 246, 247, 247, 249, 250, 251, 251, 253, 254, 255, 255, 253, 254, 255, 255, 245, 246, 247, 247, 249, 250, 251, 251, 253, 254, 255, 255, 253, 254, 255, 255 +}; +// how many bits set in compressed [0000abcd] as [0yyy0xxx] where x,y are counts +// (x looks at bits abc, y looks at bits bcd) +static const u8 compressedcounts_all_2x[] PROGMEM_FAR = { +0, 1, 17, 18, 17, 18, 34, 35, 16, 17, 33, 34, 33, 34, 50, 51 +}; +// own cell isn't included in the count (x looks at bits ac, y looks at bits bd) +static const u8 compressedcounts_outer_2x[] PROGMEM_FAR = { +0, 1, 16, 17, 1, 2, 17, 18, 16, 17, 32, 33, 17, 18, 33, 34 +}; +static const u8 compressedcounts_single[] PROGMEM_FAR = { +0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 +}; +// given [0000abcd], return mask to AND with, so that if bit a..d is set, aa..dd will be set, otherwise 00 each +static const u8 uncompressmask[] PROGMEM_FAR = { +0, 3, 12, 15, 48, 51, 60, 63, 192, 195, 204, 207, 240, 243, 252, 255 +}; + +static const u8 alive[] PROGMEM_FAR = { + 0, 0, // Any live cell with fewer than two live neighbours dies, as if caused by underpopulation. + 1, // Any live cell with two or three live neighbours lives on to the next generation. + 1|(1<<4), // Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction. + 0, 0, 0, 0, 0 // Any live cell with more than three live neighbours dies, as if by overpopulation. +}; + +static FORCEINLINE u8 sumlo(u8 a, u8 b, u8 c) +{ + u8 r = a & 1; + r += b & 1; + r += c & 1; + return r; +} + +static FORCEINLINE u8 sumhi4(u8 a, u8 b, u8 c) +{ + u8 r = a & 0x8; + r += b & 0x8; + r += c & 0x8; + return r >> 3; +} + +// scratch0/1 [0..3] hold the palette data, so anything after that is free +#ifdef _MSC_VER // fuckings to visual studio +#define ptr_compressedcounts_all_2x (scratch0 + 4) +#define ptr_compressedcounts_outer_2x (ptr_compressedcounts_all_2x + sizeof(compressedcounts_all_2x)) +#define ptr_compressedcounts_single (ptr_compressedcounts_outer_2x + sizeof(compressedcounts_outer_2x)) +#define ptr_uncompressmask (ptr_compressedcounts_single + sizeof(compressedcounts_single)) +#define ptr_alive (ptr_uncompressmask + sizeof(uncompressmask)) +#else +static constexpr u8 * ptr_compressedcounts_all_2x = scratch0 + 4; +static constexpr u8 * ptr_compressedcounts_outer_2x = ptr_compressedcounts_all_2x + sizeof(compressedcounts_all_2x); +static constexpr u8 * ptr_compressedcounts_single = ptr_compressedcounts_outer_2x + sizeof(compressedcounts_outer_2x); +static constexpr u8 * ptr_uncompressmask = ptr_compressedcounts_single + sizeof(compressedcounts_single); +static constexpr u8 * ptr_alive = ptr_uncompressmask + sizeof(uncompressmask); +static_assert(ptr_alive + sizeof(alive) < scratch1, "oops"); +#endif + + +/* // old progmem based lookup +#define RP(x, i) (pgm_read_byte(&x[i])) +#define compr(k) RP(compressbits, k) +#define advance(k) RP(advancebits, k) +#define bcount(k) RP(compressedcounts_single, k) +#define cc2ax(k) RP(compressedcounts_all_2x, k) +#define cc2ox(k) RP(compressedcounts_outer_2x, k) +#define isalive(k) RP(alive, k); +#define uncompr(k) RP(uncompressmask, k); +*/ + +// now with scratchpad memory +#define compr(k) pagelookup(scratch2, k) +#define advance(k) pagelookup(scratch3, k) +#define bcount(k) pagelookup(ptr_compressedcounts_single, k) +#define cc2ax(k) pagelookup(ptr_compressedcounts_all_2x, k) +#define cc2ox(k) pagelookup(ptr_compressedcounts_outer_2x, k) +#define isalive(k) pagelookup(ptr_alive, k) +#define uncompr(k) pagelookup(ptr_uncompressmask, k) + + +struct GolState +{ + GolState(); + + u8 **rows; + u8 w, h; + TinyRng16 rng; + u8 *wrk[2]; + bool randomize; + bool musicvis; + bool done; + bool fulldraw; + + volatile u8 paused; + u8 fontlines; + + u8 fontidx[256]; // scratch pages are completely occupied, so use an extra buf + char greets[GREETINGS_SIZE]; + + // for the draw-to-state-thing + + typedef bool ColorType; + typedef u8 DimType; + + static const DimType WIDTH = NX; + + typedef DrawFont TheFont; + TheFont font; + FontTyper typer; + + + bool _drawon; + u8 _drawx, _drawy, _drawxmin, _drawymin, _drawxmax, _drawymax; // in sub-coords + + FORCEINLINE void setColor(bool col) + { + _drawon = col; + } + + FORCEINLINE void setpoint(u8 x, u8 y) + { + u8 mask = 3 << (x & 3); + rows[y][x >> 2] |= mask; + + constexpr u16 inc = BLOCKSIZE + BORDER; + constexpr Color c = LCD::gencolor_inl(0xff,0x22,0); + LCD::fillrect(BORDER + x * inc, BORDER + y * inc, BLOCKSIZE, BLOCKSIZE, c); + } + + FORCEINLINE void fastfill_u8(u8 n) + { + const u8 xmin = _drawxmin; + const u8 ymin = _drawymin; + const u8 xmax = _drawxmax; + const u8 ymax = _drawymax; + u8 x = _drawx; + u8 y = _drawy; + bool on = _drawon; + while(n--) + { + if(on) + setpoint(x,y); + ++x; + if(x > xmax) + { + x = xmin; + ++y; + FGLCD_ASSERT(y <= ymax, "golpffy"); + } + } + _drawx = x; + _drawy = y; + } + + FORCEINLINE void setxywh(DimType x, DimType y, DimType w, DimType h) + { + _drawx = x; + _drawy = y; + _drawxmin = x; + _drawymin = y; + _drawxmax = x + w - 1; + _drawymax = y + h - 1; + } +}; + +static void ev_unpause(void *ud) +{ + GolState& gol = *(GolState*)ud; + gol.typer.setPos(4,4); + gol.paused = 0; + gol.fulldraw = true; +} + + +static uint16_t fontcb(char& c, LCD::PixelPos pp, void *ud) +{ + GolState& gol = *(GolState*)ud; + gol.paused = true; + u16 d = 20; + if(c == '\n' || c == 0) + { + d = 200; + u8 ln = gol.fontlines + 1; + gol.fontlines = ln; + if(ln >= 7 || c == 0) + { + ln = 0; + evs::schedule(1222, ev_unpause, ud); + d = 2666; + } + gol.fontlines = ln; + } + return c ? d : 0; +} + +GolState::GolState() + : font(fontidx, this) + , typer(font, greets, fontcb, this) +{ + fontlines = 0; + fulldraw = false; + + decompressRAM(&greets[0], fglcd_get_farptr(data_exo_greets), sizeof(data_exo_greets)); + + font.rebuildIndex(); // clobbered by decomp + + ev_unpause(this); // clean reset + paused = true; + typer.start(10); +} + + +static FORCEINLINE void drawcell(const u8 bits, u16 x, u16 y) +{ + LCD::setxywh_inl(x, y, BLOCKSIZE, BLOCKSIZE); + //LCD::setColor(bits ? 0xffff : 0); + LCD::setColor(palgetcolor_inl(bits)); + //LCD::setColor(LCD::gencolor(x, y, bits ? 0xff : 0)); + LCD::fastfill_inl(NPIX); +} + +static FORCEINLINE void updaterow(u8 * const next, const u8 * const row, const u8 * const above, const u8 *const below, GolState& gol, const u16 drawy) +{ + const u8 W = gol.w; + u16 drawx = XSTART; + const u16 xdrawfull = (BLOCKSIZE + BORDER) * 4u; + for(u8 x = 0; x < W; ++x) + { + // layout: + // lo hi + // [L0] [ U ] [R0] + // [L] [aabbccdd] [R] + // [L1] [ D ] [R1] + // ^------------------- need 2 highest bits of L + // ^-- need 2 lowest bits of R + const u8 C = row[x]; // [aabbccdd] + const u8 cC = compr(C); // [0000abcd] + // update center 2 cells (B and C) + const u8 U = above[x]; + const u8 D = below[x]; + const u8 cU = compr(U); + const u8 cD = compr(D); + const u8 nC = cc2ox(cC); + const u8 nU = cc2ax(cU); + const u8 nD = cc2ax(cD); + const u8 n12 = nC + nU + nD; // parallel 2x 4 bit add + const u8 nb = n12 & 0xf; + const u8 nc = n12 >> 4u; + + // count outer cells (for a and d) + const u8 L0 = above[x-1]; + const u8 L = row[x-1]; + const u8 L1 = below[x-1]; + const u8 R0 = above[x+1]; + const u8 R = row[x+1]; + const u8 R1 = below[x+1]; +#ifdef TESTMODE + if(!x) + ASSERT(!L && !L0 && !L1); + if(x+1 == W) + ASSERT(!R && !R0 && !R1); +#endif + const u8 cL0 = compr(L0); + const u8 cL = compr(L); + const u8 cL1 = compr(L1); + const u8 cR0 = compr(R0); + const u8 cR = compr(R); + const u8 cR1 = compr(R1); + + // counts for low bits of R and L's low nybble's hi bit + const u8 nL = sumhi4(cL0, cL, cL1); + const u8 nR = sumlo(cR0, cR, cR1); + const u8 bits_bcd = cC >> 1u; // drop "a" bit + const u8 nxCa = bits_bcd & 1; // isolate b bit + const u8 nxCd = (bits_bcd >> 1u) & 1; // isolate c bit + const u8 nxUa = bcount(cU & 3); + const u8 nxDa = bcount(cD & 3); + const u8 na = nL + nxCa + nxUa + nxDa; + const u8 nxUd = bcount(cU & 12); + const u8 nxDd = bcount(cD & 12); + const u8 nd = nR + nxCd + nxUd + nxDd; + + // upper 4 bits are unconditionally applied (always alive), lower 4 bits depend on current state (stay alive) + u8 cx = isalive(nd); + cx <<= 1u; + cx |= isalive(nc); + cx <<= 1u; + cx |= isalive(nb); + cx <<= 1u; + cx |= isalive(na); + // become alive | stay alive + const u8 lifebits = (cx >> 4u) | (cx & cC); + + const u8 lifemask = uncompr(lifebits); + const u8 cadv = advance(C); + const u8 cnext = cadv & lifemask; + next[x] = cnext; + + // -------- draw ------------- + u8 cell = cnext; + const u8 xinc = BLOCKSIZE + BORDER; + u16 dx = drawx; + if(UNLIKELY(gol.fulldraw)) + { + u8 k = 0; + for( ; k < 4; cell >>= 2u, dx += xinc, ++k) + drawcell(cell & 3, dx, drawy); + } + else if(u8 changebits = compr(cnext ^ C)) + { + for( ; changebits; cell >>= 2u, dx += xinc, changebits >>= 1u) + if(changebits & 1) + drawcell(cell & 3, dx, drawy); + } + + drawx += xdrawfull; + } +} + +static FORCEINLINE void updatefield(GolState& gol) +{ + u8 **rows = &gol.rows[0]; + u8 **wrk = &gol.wrk[0]; + const u8 W = gol.w; + const u8 H = gol.h; + u16 drawy = YSTART; + // invariants: + // wrk[0] is written to + // wrk[1] contains above row as it was before it was updated + // zero rows at rows[-1] and rows[W-1] are never touched or exchanged + + { + const u8 * const zero = rows[-1]; + u8 * const newrow = wrk[0]; + u8 * const tmp = wrk[1]; // unused + u8 * const oldrow = rows[0]; + const u8 * const below = rows[1]; // yet untouched + updaterow(newrow, oldrow, zero, below, gol, drawy); + wrk[0] = tmp; + wrk[1] = oldrow; + rows[0] = newrow; + } + + for(u8 y = 1; y < H; ++y) + { + drawy += BLOCKSIZE + BORDER; + u8 * const newrow = wrk[0]; + u8 * const oldrow = rows[y]; + u8 * const oldabove = wrk[1]; + const u8 * const below = rows[y+1]; // yet untouched + updaterow(newrow, oldrow, oldabove, below, gol, drawy); + wrk[0] = oldabove; // don't need this anymore + wrk[1] = oldrow; // will be next row's oldabove + rows[y] = newrow; // just written data + } +} + +/* +static FORCEINLINE void drawline(const Color *pal, const u8 * const row, const u8 W, const u8 sz, u16 x, const u16 y) +{ + const u16 N = u16(sz)*sz; + const u8 xinc = sz+BORDER; + for(u8 i = 0; i < W; ++i) + { + u8 cell = row[i]; + for(u8 j = 0; j < 4; ++j, cell >>= 2u, x += xinc) + drawcell(pal, cell & 3, x, y, sz, N); + } +#ifdef TESTMODE + putchar('\n'); +#endif +} + +static NOINLINE void drawfield(const Color *pal, const u8 * const * const rows, const u8 W, const u8 H, const u8 sz, const u16 x, u16 y) +{ +#ifdef TESTMODE + HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); + COORD co { 0, 0 }; + SetConsoleCursorPosition(h, co); +#endif + + const u8 yinc = sz+BORDER; + for(u8 i = 0; i < H; ++i, y += yinc) + drawline(pal, rows[i]+1, W, sz, x, y); +} +*/ + +static void rndfield(GolState& gol) +{ + u8 *const* const rows = gol.rows; + const u8 w = gol.w; + const u8 h = gol.h; + TinyRng16 rng = gol.rng; + for(u8 y = 0; y < h; ++y) + { + u8 * const row = rows[y]; + for(u8 x = 0; x < w; ++x) + { + const u8 b = rng() & 0x55; + row[x] = b | (b << 1); + } + } + gol.rng = rng; +} + +static void musicvis(GolState& gol) +{ +#ifdef CFG_ENABLE_AUDIO + const u8 w = gol.w; + const u8 h = gol.h; + u16 y = 1; + for(u8 i = 0; i < pmfplayer_max_channels && y < h; ++i, y += 4) + { + const pmf_channel_info ch = G.mus.player.channel_info(i); + if(ch.note_hit & 1) + { + u8 *row = gol.rows[y]; + const u8 note = ch.base_note & 0x7f; + u8 lim = (note+7) / 8; + if(lim > w) + lim = w; + for(u8 x = 1; x < lim; ++x) + { + row[x] = 0xff; + } + } + } +#endif +} + +static void ev_randomize(void *pstate) +{ + GolState& gol = *(GolState*)pstate; + if(gol.done) + return; + + gol.randomize = true; + evs::schedule(8000, ev_randomize, pstate); +} + +static void ev_musicvis(void *pstate) +{ + GolState& gol = *(GolState*)pstate; + if(gol.done) + return; + gol.musicvis = true; + evs::schedule(16, ev_musicvis, pstate); +} + +static const Color golpal_PF[] PROGMEM_FAR = +{ + 0, + 0xffff, + LCD::gencolor_inl(0, 0xcf, 0xff), + LCD::gencolor_inl(0x10,0x20, 0xff) +}; + + +demopart part_gol() +{ + // --- scratchpad setup --- + static_assert(sizeof(compressbits) == 256, "aa"); + static_assert(sizeof(advancebits) == 256, "aa"); + + #define LOADFAR(dst, src, n) fglcd::ProgmemFar::Memcpy(dst, fglcd_get_farptr(src), n) + + LOADFAR(scratch2, compressbits, 256); + LOADFAR(scratch3, advancebits, 256); + + LOADFAR(ptr_compressedcounts_all_2x, compressedcounts_all_2x, sizeof(compressedcounts_all_2x)); + LOADFAR(ptr_compressedcounts_outer_2x, compressedcounts_outer_2x, sizeof(compressedcounts_outer_2x)); + LOADFAR(ptr_compressedcounts_single, compressedcounts_single, sizeof(compressedcounts_single)); + LOADFAR(ptr_uncompressmask, uncompressmask, sizeof(uncompressmask)); + LOADFAR(ptr_alive, alive, sizeof(alive)); + + // --- end scratchpad --- + + + auto golpal = farload(golpal_PF); + applyPal16_RAM(&golpal[0], golpal.N); + + u8 ** const rows = (u8**)alloca(NY * sizeof(u8*)); + u8 * const field = (u8*)alloca(NBUF*2); + u8 * const zero = (u8*)alloca(NX); + u8 * const workmem1 = (u8*)alloca(NX); + u8 * const workmem2 = (u8*)alloca(NX); + fglcd::RAM::Memset(zero, 0, NX); + fglcd::RAM::Memset(field, 0, NBUF); + fglcd::RAM::Memset(workmem1, 0, NX); + fglcd::RAM::Memset(workmem2, 0, NX); + + { + u8 *p = &field[1]; + for(u8 y = 1; y <= H; ++y, p += NX) + { + FGLCD_ASSERT(p < field+NBUF, "golptr"); + rows[y] = p; + } + rows[0] = zero; + rows[NY-1] = zero; + } + + GolState gol; + gol.w = u8(W); + gol.h = u8(H); + gol.rows = &rows[1]; + gol.done = false; + gol.wrk[0] = &workmem1[1]; + gol.wrk[1] = &workmem2[1]; + gol.randomize = false; + gol.musicvis = false; + gol.rng.init(1337); + + // field init done + + //ev_randomize(&gol); + //ev_musicvis(&gol); + //gol.randomize = true; + + interp::Interpolator<1> inp(16); + interp::FancyInterpolatedValue fadeout(0); + + bool exiting = false; + partdone = false; + while(true) + { + if(!exiting && partdone) + { + exiting = true; + inp.add(fadeout); + fadeout.interpolateTo(255, 1300); + } + + if(exiting) + { + u8 fade = fadeout; + if(fade == 255) + break; + for(u8 i = 0; i < golpal.N; ++i) + palsetcolor(i, dampenColor(golpal[i], fade)); + gol.fulldraw = true; + } + + gol.typer.update(); + + /*if(gol.randomize) + { + gol.randomize = false; + rndfield(gol); + }*/ + /*if(gol.musicvis) + { + gol.musicvis = false; + musicvis(gol); + }*/ + if(!gol.paused) + { + updatefield(gol); + gol.fulldraw = false; + } + } + gol.done = true; + partdone = false; +} diff --git a/democode/music.gen.hh b/democode/music.gen.hh new file mode 100644 index 0000000..5e8de97 --- /dev/null +++ b/democode/music.gen.hh @@ -0,0 +1,98 @@ +// Song name: (music.xm) +// Length: 93 +// Channels: 9 +// Size: 23765 bytes +// Exporter: PMF Converter v0.6 (PMF v1.4) +0x70, 0x6d, 0x66, 0x78, 0x00, 0x14, 0x01, 0x00, 0xd5, 0x5c, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x1a, 0x01, 0x00, 0x00, 0x52, 0x05, 0x00, 0x00, 0x86, 0x05, 0x00, 0x00, 0x86, 0x05, 0x00, 0x00, 0x06, 0x7d, 0x1c, 0x00, 0x00, 0x6b, 0x5d, 0x00, 0x09, 0x36, 0x05, 0x05, 0x00, 0x01, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x08, 0x09, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0a, 0x0e, 0x0c, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x16, 0x18, 0x19, 0x15, 0x17, 0x16, 0x17, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x33, 0x17, 0x15, 0x17, 0x20, 0x1b, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2b, 0x32, 0x2b, 0x2a, 0x2b, 0x2a, 0x2b, 0x2a, 0x2b, 0x2a, 0x2b, 0x2a, 0x2b, 0x2f, 0x30, 0x31, 0x34, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x11, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x0a, 0x00, 0xff, 0xdd, 0x11, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x0a, 0x00, 0xff, 0x1e, 0x12, 0x00, 0x00, 0x41, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0e, 0x00, 0xff, 0x60, 0x22, 0x00, 0x00, 0x39, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0e, 0x00, 0xff, 0x9a, 0x3f, 0x00, 0x00, 0x3a, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0e, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x02, 0xff, 0x80, 0x01, 0x00, 0x0e, 0x00, 0xff, 0xff, 0x00, 0x02, 0xff, 0x80, 0x02, 0x00, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x03, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x04, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x86, 0x05, 0x9a, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0xcd, 0x05, 0xd9, 0x05, 0xec, 0x05, 0x1d, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x4d, 0x06, 0xec, 0x05, 0x1d, 0x06, 0x61, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0xec, 0x05, 0x1d, 0x06, 0x6e, 0x06, 0x80, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0xcd, 0x05, 0xd9, 0x05, 0xec, 0x05, 0x1d, 0x06, 0x6e, 0x06, 0x80, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x8a, 0x06, 0xd9, 0x05, 0xec, 0x05, 0x1d, 0x06, 0x6e, 0x06, 0x80, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0xec, 0x05, 0x1d, 0x06, 0x6e, 0x06, 0x96, 0x06, 0xc2, 0x06, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0xcd, 0x05, 0xd9, 0x05, 0xec, 0x05, 0x1d, 0x06, 0x6e, 0x06, 0x96, 0x06, 0xee, 0x06, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0xec, 0x05, 0x1d, 0x06, 0x6e, 0x06, 0x1d, 0x07, 0x48, 0x07, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0xa8, 0x07, 0x6e, 0x06, 0xdd, 0x07, 0x0f, 0x08, 0x1b, 0x08, 0x27, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0x33, 0x08, 0x6e, 0x06, 0x68, 0x08, 0x0f, 0x08, 0x1b, 0x08, 0x92, 0x08, 0x3f, 0x00, 0xcd, 0x05, 0xd9, 0x05, 0x77, 0x07, 0x9e, 0x08, +0x6e, 0x06, 0xd3, 0x08, 0x0f, 0x08, 0xff, 0x08, 0x92, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0xa8, 0x07, 0x6e, 0x06, 0x0b, 0x09, 0x0f, 0x08, 0x1b, 0x08, 0x27, 0x08, 0x3f, 0x00, 0xcd, 0x05, 0xd9, 0x05, 0x77, 0x07, 0xa8, 0x07, 0x6e, 0x06, 0xdd, 0x07, 0x0f, 0x08, 0x1b, 0x08, 0x27, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0x9e, 0x08, 0x6e, 0x06, 0xd3, 0x08, 0x0f, 0x08, 0xff, 0x08, 0x92, 0x08, 0x3f, 0x00, 0xcd, 0x05, 0xd9, 0x05, 0x77, 0x07, 0xa8, 0x07, 0x6e, 0x06, 0x23, 0x09, 0x0f, 0x08, 0x1b, 0x08, 0x27, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0x9e, 0x08, 0x6e, 0x06, 0x54, 0x09, 0x0f, 0x08, 0xff, 0x08, 0x92, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0x33, 0x08, 0x6e, 0x06, 0x7d, 0x09, 0x0f, 0x08, 0x1b, 0x08, 0x92, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0xa8, 0x07, 0x6e, 0x06, 0xa6, 0x09, 0x0f, 0x08, 0x1b, 0x08, 0x27, 0x08, 0x3f, 0x00, 0xcd, 0x05, 0xbe, 0x09, 0xc9, 0x09, 0xf7, 0x09, 0x10, 0x0a, 0x1b, 0x0a, 0x31, 0x0a, 0x3d, 0x0a, 0x49, 0x0a, 0x3f, 0x00, 0xcd, 0x05, 0x55, 0x0a, 0x67, 0x0a, 0xad, 0x0a, 0xd2, 0x0a, 0x1b, 0x0a, 0x31, 0x0a, 0x3d, 0x0a, 0x49, 0x0a, 0x3f, 0x00, 0xcd, 0x05, 0xd9, 0x05, 0x77, 0x07, 0xe1, 0x0a, 0x1a, 0x0b, 0x29, 0x0b, 0x0f, 0x08, 0x1b, 0x08, 0x27, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0x7c, 0x0b, 0x1a, 0x0b, 0xb4, 0x0b, 0x0f, 0x08, 0x1b, 0x08, 0x92, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0x07, 0x0c, 0x1a, 0x0b, 0x3f, 0x0c, 0x0f, 0x08, 0xff, 0x08, 0x92, 0x08, 0x3f, 0x00, 0x92, 0x0c, 0xd9, 0x05, +0x77, 0x07, 0xe1, 0x0a, 0x1a, 0x0b, 0x29, 0x0b, 0x0f, 0x08, 0x1b, 0x08, 0x27, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0x07, 0x0c, 0x1a, 0x0b, 0x3f, 0x0c, 0x0f, 0x08, 0xff, 0x08, 0x92, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0x7c, 0x0b, 0x1a, 0x0b, 0x9e, 0x0c, 0x0f, 0x08, 0x1b, 0x08, 0x92, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0xf1, 0x0c, 0x1a, 0x0b, 0x29, 0x0d, 0x27, 0x08, 0x7d, 0x0d, 0x89, 0x0d, 0x3f, 0x00, 0xcd, 0x05, 0x00, 0x00, 0x95, 0x0d, 0xbd, 0x0d, 0x00, 0x00, 0x29, 0x0b, 0x0f, 0x08, 0x1b, 0x08, 0x27, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xf2, 0x0d, 0x95, 0x0d, 0x01, 0x0e, 0x00, 0x00, 0xb4, 0x0b, 0x0f, 0x08, 0x1b, 0x08, 0x92, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xf2, 0x0d, 0x95, 0x0d, 0x36, 0x0e, 0x1a, 0x0b, 0x3f, 0x0c, 0x0f, 0x08, 0xff, 0x08, 0x92, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x6b, 0x0e, 0x93, 0x0e, 0x1a, 0x0b, 0xb4, 0x0b, 0x0f, 0x08, 0x1b, 0x08, 0x92, 0x08, 0x3f, 0x00, 0xcd, 0x05, 0xd9, 0x05, 0x77, 0x07, 0x7c, 0x0b, 0x1a, 0x0b, 0x9e, 0x0c, 0x0f, 0x08, 0x1b, 0x08, 0x92, 0x08, 0x3f, 0x00, 0xcd, 0x05, 0xd9, 0x05, 0x77, 0x07, 0xe1, 0x0a, 0x1a, 0x0b, 0xcd, 0x0e, 0x0f, 0x08, 0x1b, 0x08, 0x27, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0x7c, 0x0b, 0x1a, 0x0b, 0x0f, 0x0f, 0x0f, 0x08, 0x1b, 0x08, 0x92, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0xe1, 0x0a, 0x1a, 0x0b, 0x3f, 0x0f, 0x0f, 0x08, 0x1b, 0x08, 0x27, 0x08, 0x3f, 0x00, 0xcd, 0x05, 0xd9, 0x05, 0x77, 0x07, 0x7c, 0x0b, 0x1a, 0x0b, 0x58, 0x0f, 0x0f, 0x08, 0x1b, 0x08, 0x92, 0x08, 0x3f, 0x00, +0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0xe1, 0x0a, 0x1a, 0x0b, 0x88, 0x0f, 0x0f, 0x08, 0x1b, 0x08, 0x27, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0xe1, 0x0a, 0x1a, 0x0b, 0xa1, 0x0f, 0x0f, 0x08, 0x1b, 0x08, 0x27, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0x07, 0x0c, 0x1a, 0x0b, 0x0b, 0x09, 0x0f, 0x08, 0xff, 0x08, 0x92, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0x7c, 0x0b, 0x1a, 0x0b, 0xcd, 0x0f, 0x0f, 0x08, 0x1b, 0x08, 0x92, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0xf1, 0x0c, 0x1a, 0x0b, 0xf9, 0x0f, 0x27, 0x08, 0x7d, 0x0d, 0x89, 0x0d, 0x3f, 0x00, 0x00, 0x00, 0xf2, 0x0d, 0x1b, 0x10, 0xe1, 0x0a, 0x00, 0x00, 0x29, 0x0b, 0x0f, 0x08, 0x1b, 0x08, 0x27, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xf2, 0x0d, 0x1b, 0x10, 0x7c, 0x0b, 0x00, 0x00, 0xb4, 0x0b, 0x0f, 0x08, 0x1b, 0x08, 0x92, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xf2, 0x0d, 0x1b, 0x10, 0x07, 0x0c, 0x00, 0x00, 0x3f, 0x0c, 0x0f, 0x08, 0xff, 0x08, 0x92, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xf2, 0x0d, 0x1b, 0x10, 0x7c, 0x0b, 0x00, 0x00, 0x9e, 0x0c, 0x0f, 0x08, 0x1b, 0x08, 0x92, 0x08, 0x3f, 0x00, 0xcd, 0x05, 0xf2, 0x0d, 0x1b, 0x10, 0xe1, 0x0a, 0x00, 0x00, 0x29, 0x0b, 0x0f, 0x08, 0x1b, 0x08, 0x27, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xf2, 0x0d, 0x30, 0x10, 0x43, 0x10, 0x00, 0x00, 0x3f, 0x0c, 0x0f, 0x08, 0xff, 0x08, 0x92, 0x08, 0x3f, 0x00, 0xcd, 0x05, 0x72, 0x10, 0x00, 0x00, 0x82, 0x10, 0x00, 0x00, 0xb0, 0x10, 0x0f, 0x08, 0x1b, 0x08, 0x92, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xbe, 0x09, 0x01, 0x11, 0x0d, 0x11, 0x26, 0x11, 0x31, 0x11, 0x4b, 0x11, 0x66, 0x11, +0x81, 0x11, 0x3f, 0x00, 0xcd, 0x05, 0xf2, 0x0d, 0x1b, 0x10, 0xe1, 0x0a, 0x00, 0x00, 0x29, 0x0b, 0x0f, 0x08, 0x1b, 0x08, 0x27, 0x08, 0x3f, 0x00, 0x00, 0x00, 0xd9, 0x05, 0x77, 0x07, 0xe1, 0x0a, 0x1a, 0x0b, 0x29, 0x0b, 0x0f, 0x08, 0x1b, 0x08, 0x27, 0x08, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x80, 0x0d, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x24, 0x79, 0x00, 0x00, 0x00, 0x75, 0x01, 0x46, 0x00, 0x01, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x20, 0x0c, 0x00, 0xd6, 0x03, 0x4f, 0xa0, 0x20, 0x97, 0x68, 0x3c, 0xdc, 0x89, 0x8e, 0x29, 0xf6, 0x78, 0xc7, 0xa9, 0xe0, 0x88, 0xc6, 0xc3, 0x9d, 0xe8, 0x98, 0x62, 0x8f, 0x77, 0x9c, 0x0a, 0x8e, 0x68, 0x3c, 0xdc, 0x89, 0x8e, 0x29, 0xf6, 0x78, 0xc7, 0xa9, 0xe0, 0x88, 0xc6, 0xc3, 0x9d, 0xe8, 0x98, 0x62, 0x8f, 0x77, 0x9c, 0x02, 0xf1, 0x90, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x46, 0x00, 0xc2, 0x00, 0xc2, 0x00, 0xc2, 0x00, 0xc2, 0x00, 0xc2, 0x00, 0xc2, 0x00, 0xc2, 0x00, 0x00, 0xd6, 0x01, 0x4f, 0xa0, 0x88, 0xc6, 0xc3, 0x9d, 0xe8, 0x98, 0x62, 0x8f, 0x77, 0x9c, 0x0a, 0x8e, 0x68, 0x3c, 0xdc, 0x89, +0x8e, 0x29, 0xf6, 0x78, 0xc7, 0xa9, 0xe0, 0x88, 0xc6, 0xc3, 0x9d, 0xe8, 0x98, 0x62, 0x8f, 0x77, 0x9c, 0x0a, 0x8e, 0x68, 0x3c, 0xdc, 0x89, 0x8e, 0x29, 0xf6, 0x78, 0xc7, 0x29, 0x06, 0xd0, 0x00, 0x03, 0xa0, 0x10, 0x4c, 0x04, 0x00, 0x40, 0x30, 0x14, 0x42, 0x0a, 0x08, 0x60, 0x06, 0x63, 0x04, 0x08, 0x86, 0x42, 0x48, 0x01, 0x01, 0xcc, 0x60, 0x8c, 0x00, 0xc1, 0x50, 0x08, 0x29, 0x20, 0x80, 0x19, 0x8c, 0xb1, 0x14, 0x12, 0x05, 0x44, 0x58, 0xf8, 0x47, 0x1c, 0xf0, 0x0e, 0x75, 0x00, 0x46, 0x00, 0xc2, 0x00, 0xc2, 0x00, 0xc2, 0x00, 0xc2, 0x00, 0xc2, 0x00, 0xc2, 0x80, 0x30, 0xc2, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0xde, 0x00, 0x20, 0x0c, 0x00, 0x05, 0x70, 0x80, 0x37, 0x00, 0x00, 0xc2, 0x00, 0x00, 0xc2, 0x00, 0x08, 0x03, 0xc2, 0x20, 0x8c, 0x30, 0x00, 0x31, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x90, 0x04, 0x00, 0x00, 0x00, 0x00, 0xd6, 0x42, 0x2e, 0x98, 0x07, 0x82, 0xa5, 0xd0, 0x22, 0x0e, 0x31, 0xc7, 0x58, 0xc4, 0x27, 0x46, 0xb1, 0xc8, 0x38, 0x44, 0x20, 0xd2, 0xf0, 0x86, 0x39, 0x66, 0x2c, 0xe2, 0x10, 0x73, 0x8c, 0x45, 0x7c, 0x62, 0x14, 0x8b, 0x8c, 0x43, 0x04, 0x52, 0x2c, 0x17, 0x79, 0x08, 0xd6, 0xc3, 0x30, 0xa0, 0xe6, 0x81, 0xe0, 0x29, 0x3c, 0xc7, 0x58, 0xc4, 0x21, 0xe6, 0x18, 0x8b, 0xf8, 0xc4, 0x28, 0x16, 0x19, 0x87, 0x08, 0x44, 0x1a, 0xde, 0x30, 0xc7, 0x8c, 0x45, 0x1c, 0x62, 0x8e, 0xb1, 0x88, 0x4f, 0x8c, 0x62, 0x91, 0x71, 0x88, 0x40, 0x8a, 0x07, 0xd6, 0x43, 0x31, 0xa0, 0xe6, 0x81, 0xc2, 0x73, 0x04, 0x4f, 0xf1, 0x14, 0x3e, 0x64, 0xc6, 0x22, 0x3e, 0x31, +0x8a, 0x45, 0x82, 0xe7, 0x21, 0x03, 0x99, 0xc6, 0x37, 0xce, 0x21, 0x72, 0x91, 0x87, 0x9c, 0x43, 0x2e, 0xf2, 0x93, 0xa3, 0x5c, 0x44, 0x1e, 0x32, 0x90, 0xe2, 0x29, 0xbc, 0x08, 0xde, 0x42, 0x2e, 0x98, 0x07, 0x82, 0xa5, 0xd0, 0x22, 0x0e, 0x31, 0xc7, 0x58, 0xc4, 0x27, 0x46, 0xb1, 0xc8, 0x38, 0x44, 0x20, 0xd2, 0xf0, 0x86, 0x39, 0x66, 0x2c, 0xe2, 0x90, 0x5a, 0x98, 0x00, 0x18, 0x17, 0x61, 0x54, 0xc0, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x02, 0xd6, 0xc3, 0x30, 0xa0, 0xe6, 0x81, 0xe0, 0x29, 0x3c, 0xc7, 0x58, 0xc4, 0x21, 0xe6, 0x18, 0x8b, 0xf8, 0xc4, 0x28, 0x16, 0x19, 0x87, 0x08, 0x44, 0x1a, 0xde, 0x30, 0xc7, 0x4c, 0xb0, 0x14, 0xca, 0x43, 0x20, 0x16, 0x11, 0x89, 0x4c, 0x8c, 0x22, 0x15, 0xb1, 0xd8, 0x45, 0x30, 0x92, 0x01, 0xd6, 0x01, 0x4f, 0xa0, 0x88, 0xc6, 0xc3, 0x9f, 0xe8, 0x98, 0x62, 0x8f, 0x7f, 0x9c, 0x0a, 0x8e, 0x68, 0x3c, 0xfc, 0x89, 0x8e, 0x29, 0xf6, 0xf8, 0xc7, 0xa9, 0xe0, 0x88, 0xc6, 0xc3, 0x9f, 0xe8, 0x98, 0x62, 0x8f, 0x7f, 0x9c, 0x0a, 0x8e, 0x68, 0x3c, 0xfc, 0x89, 0x8e, 0x29, 0xf6, 0xf8, 0xc7, 0x29, 0x06, 0x0d, 0x84, 0x00, 0x0a, 0xa9, 0x50, 0x4c, 0x04, 0x00, 0x14, 0xd2, 0xc2, 0x10, 0xc5, 0x04, 0x04, 0x85, 0xe4, 0xb0, 0x82, 0xa1, 0x01, 0x82, 0xe1, 0x56, 0x40, 0x30, 0x85, 0x54, 0x30, 0x34, 0x98, 0xa3, 0x18, 0x6e, 0x05, 0x04, 0x53, 0x48, 0x05, 0x43, 0x97, 0xd2, 0xa2, 0x80, 0x08, 0x0b, 0xff, 0x88, 0x03, 0xde, 0x01, 0x16, 0x02, 0xc6, 0x05, 0xa8, 0x0d, 0x32, 0x83, 0xbf, 0xd0, 0x68, 0x80, 0x3a, 0x55, 0x55, 0x2d, 0x30, 0xff, 0xff, 0x17, 0x1b, 0x60, 0x90, 0x17, 0x3a, 0x0d, 0xb0, 0xc8, 0x0b, 0xa5, 0x06, 0x58, 0x60, 0xfe, 0xff, +0x42, 0xa7, 0x01, 0x16, 0xf9, 0x5f, 0x28, 0xd3, 0x67, 0x80, 0x05, 0xe6, 0xff, 0xff, 0x07, 0xf1, 0x5a, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x60, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x68, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0d, 0x74, 0x00, 0x0a, 0x89, 0x50, 0x4c, 0x04, 0x00, 0x14, 0x92, 0xc2, 0x0e, 0xc5, 0x04, 0x04, 0x85, 0xd4, 0x30, 0x82, 0x9d, 0x01, 0x82, 0xe1, 0x54, 0x40, 0x30, 0x85, 0x44, 0xb0, 0x33, 0x58, 0xa3, 0x18, 0x4e, 0x05, 0x04, 0x53, 0x48, 0x04, 0x3b, 0x97, 0x92, 0xa2, 0x80, 0x08, 0x0b, 0xff, 0x88, 0x03, 0xde, 0x01, 0xd6, 0x42, 0x34, 0xc0, 0xb8, 0x00, 0x55, 0x55, 0x55, 0x55, 0xb5, 0xc0, 0xfc, 0xff, 0x5f, 0x28, 0x35, 0x40, 0x7d, 0xaa, 0x05, 0xe6, 0x0b, 0xa5, 0x06, 0xa8, 0x45, 0xfe, 0x85, 0x4e, 0x03, 0xd4, 0x02, 0xf3, 0x7f, 0xa1, 0xcf, 0x00, 0xb5, 0xc8, 0xff, 0x1f, 0xf1, 0x6a, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0d, 0x68, 0x00, 0x0a, 0x71, 0x50, 0x4c, 0x04, 0x00, 0x14, 0x62, 0x42, 0x0d, 0xc5, 0x04, 0x04, 0x85, 0xc8, 0xd0, 0x81, 0x9a, 0x01, 0x82, 0x61, 0x53, 0x40, 0x30, 0x85, 0x38, 0x50, 0x33, 0x28, 0xa3, 0x18, 0x36, 0x05, 0x04, 0x53, 0x88, 0x03, 0x35, 0x97, 0x62, 0xa2, 0x80, 0x08, 0x0b, 0xff, 0x88, 0x03, 0xde, 0x01, 0xd6, 0x42, 0x34, 0xc0, 0xb8, 0x00, 0x55, 0x55, 0x55, 0x55, 0xb5, 0xc0, 0xfc, 0xff, 0x5f, 0x28, 0x35, 0x40, 0x7d, 0xaa, 0x05, 0xe6, 0x0b, 0xa5, 0x06, 0xa8, 0x45, 0xfe, 0x85, 0x4e, 0x03, 0xd4, 0x02, 0xf3, 0x5f, 0x28, 0xd3, 0x67, 0x80, 0x5a, 0x60, 0xfe, 0xff, 0x07, 0xf1, +0x64, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6, 0x42, 0x34, 0xc0, 0xb8, 0x00, 0x55, 0x55, 0x55, 0xb5, 0xc0, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x16, 0x02, 0xc6, 0x05, 0xa8, 0x0d, 0x32, 0x83, 0xbf, 0xd0, 0x68, 0x80, 0x3a, 0x55, 0x55, 0x2d, 0x30, 0xff, 0xff, 0x17, 0x1b, 0x60, 0x90, 0x17, 0x6a, 0x0d, 0xb0, 0xc8, 0x0b, 0xa5, 0x06, 0x58, 0x60, 0xfe, 0xff, 0x42, 0xad, 0x01, 0x16, 0xf9, 0x7f, 0xa1, 0xd8, 0x00, 0x0b, 0xcc, 0xff, 0xff, 0x0f, 0xd6, 0x42, 0x36, 0xc0, 0xb8, 0x00, 0x55, 0x55, 0x55, 0x55, 0xb5, 0xc0, 0xfc, 0xff, 0x5f, 0x6c, 0x80, 0x5a, 0x28, 0xf6, 0x8b, 0xdc, 0x1a, 0xa0, 0x16, 0x98, 0xff, 0x0b, 0xa5, 0x06, 0xa8, 0x45, 0xfe, 0x85, 0x4e, 0x03, 0xd4, 0x02, 0xf3, 0xff, 0x3f, 0xd6, 0x42, 0x36, 0xc0, 0xb8, 0x00, 0x55, 0x55, 0x55, 0x55, 0xb5, 0xc0, 0xfc, 0xff, 0x5f, 0x6c, 0x80, 0x5a, 0xe8, 0xf6, 0x8b, 0x3c, 0x1b, 0xa0, 0x16, 0x98, 0xff, 0x0b, 0xc5, 0x06, 0xa8, 0x45, 0xfe, 0x85, 0x5a, 0x03, 0xd4, 0x02, 0xf3, 0xff, 0x3f, 0xd6, 0x02, 0x36, 0xc0, 0xb8, 0x00, 0x55, 0x55, 0x55, 0x55, 0xb5, 0xc0, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x71, 0x60, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6, 0x01, 0x4f, 0xf8, 0x23, 0xf0, 0xa3, 0xe1, 0xd7, 0xc5, 0xb7, 0xa5, 0x97, 0x8d, 0x7f, 0x6d, 0x5f, 0x4d, 0x3f, 0x2d, 0x1f, 0x0d, 0xff, 0xec, 0xde, 0xcc, 0xbe, 0xac, 0x9e, 0x94, 0x86, 0x74, 0x66, 0x54, 0x46, 0x34, 0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6, 0x40, 0x0b, 0x08, 0x81, 0x00, 0x02, 0x54, 0x55, +0xa7, 0x01, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0x71, 0x78, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6, 0x42, 0x2b, 0xc0, 0xb9, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0xf1, 0x42, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x48, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x46, 0x00, 0x00, 0xc2, 0x00, 0x00, 0xc2, 0x00, 0x00, 0xc2, 0x00, 0xc2, 0x20, 0x8c, 0x30, 0x00, 0xd6, 0x01, 0x4f, 0x70, 0x22, 0x60, 0xa2, 0x51, 0x46, 0x74, 0x66, 0x54, 0x46, 0x74, 0x66, 0x54, 0x46, 0x74, 0x66, 0x54, 0x46, 0x74, 0x66, 0x54, 0x46, 0x74, 0x66, 0x54, 0x46, 0x74, 0x66, 0x54, 0x46, 0x74, 0x66, 0x54, 0x46, 0x74, 0x66, 0x54, 0x46, 0x74, 0x66, 0x54, 0x46, 0x74, 0x66, 0x54, 0x46, 0x74, 0x66, 0x54, 0x46, 0x74, 0x66, 0x54, 0x46, 0x74, 0x66, 0x54, 0x46, 0x74, 0x66, 0x54, 0x46, 0x74, 0x66, 0x54, 0x46, 0x00, 0xd6, 0x40, 0x05, 0x88, 0x00, 0x08, 0x86, 0x80, 0x98, 0x42, 0x42, 0x0c, 0x14, 0x13, 0xd0, 0x42, 0x30, 0x0c, 0x14, 0x83, 0x1c, 0x10, 0x8c, 0x4c, 0x30, 0x0c, 0x14, 0x13, 0xd0, 0x42, 0x30, 0x14, 0x13, 0xd0, 0xee, 0x06, 0x33, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x80, 0x37, 0x00, 0xc2, 0x20, 0x8c, 0x30, 0x00, 0x16, 0x01, 0x40, 0x03, 0x21, 0x80, 0x42, 0x2a, 0x14, 0x13, 0x10, 0x4c, 0x30, 0x14, 0x13, 0x10, 0x14, 0xd2, 0xa2, 0x18, 0x0e, 0x05, 0x04, 0x10, 0x0c, 0xc5, 0x04, 0x04, 0xc1, 0x50, 0x08, +0x03, 0x2b, 0x01, 0xc1, 0x2d, 0x8a, 0xa1, 0x90, 0x50, 0x40, 0x50, 0x0c, 0x85, 0x54, 0x38, 0xa0, 0x52, 0x5a, 0x14, 0x10, 0x61, 0xe1, 0x1f, 0x71, 0xc0, 0x3b, 0x05, 0x70, 0x80, 0x37, 0x00, 0x00, 0xc2, 0x00, 0x00, 0xc2, 0x00, 0x00, 0xc2, 0x00, 0x00, 0xd6, 0x03, 0x32, 0xf8, 0x31, 0xae, 0xc0, 0x7c, 0xf1, 0x41, 0x06, 0x68, 0xf1, 0x41, 0x16, 0x7e, 0x2c, 0x32, 0x40, 0x0b, 0x47, 0x16, 0x98, 0xff, 0x18, 0x60, 0x91, 0x17, 0x8e, 0x0c, 0xf0, 0xa1, 0x05, 0xe6, 0x2f, 0x1c, 0x19, 0xe0, 0x40, 0x0b, 0xcc, 0x5f, 0x7c, 0x80, 0x85, 0x23, 0x8b, 0x0c, 0xfe, 0x00, 0xbd, 0xc8, 0xc2, 0x8f, 0x05, 0xe6, 0x03, 0xb4, 0x70, 0x64, 0x91, 0x1f, 0x03, 0x2c, 0x30, 0x7f, 0xe1, 0xc8, 0x00, 0x1f, 0x5a, 0x60, 0xfe, 0xc2, 0x91, 0x01, 0x0e, 0xb4, 0xc0, 0xfc, 0xc5, 0x07, 0x08, 0x06, 0x0d, 0x74, 0x00, 0x0a, 0x89, 0x50, 0x4c, 0x04, 0x00, 0x4c, 0x30, 0x14, 0x13, 0x10, 0x14, 0x92, 0xa2, 0x18, 0xee, 0x04, 0x04, 0x10, 0x0c, 0xc5, 0x04, 0x04, 0xc1, 0x50, 0x08, 0x03, 0x23, 0x01, 0xc1, 0x29, 0x8a, 0xa1, 0x90, 0x4e, 0x40, 0x50, 0x0c, 0x85, 0x44, 0x38, 0xa0, 0x52, 0x52, 0x14, 0x10, 0x61, 0xe1, 0x1f, 0x71, 0xc0, 0x3b, 0xd6, 0x43, 0x31, 0xf8, 0x31, 0xae, 0xc0, 0x7c, 0xf1, 0x41, 0x06, 0x68, 0xf1, 0x41, 0x16, 0x3e, 0x2c, 0x32, 0x40, 0x0b, 0x2f, 0x16, 0x98, 0x7f, 0x18, 0x60, 0x91, 0x17, 0x5e, 0x0c, 0x70, 0xa0, 0x05, 0xe6, 0x2f, 0xbc, 0x18, 0xe0, 0x3c, 0x0b, 0xcc, 0x5f, 0x7c, 0x80, 0x85, 0x17, 0x8b, 0x0c, 0xfe, 0x00, 0xbd, 0xc8, 0xc2, 0x87, 0x05, 0xe6, 0x03, 0xb4, 0xf0, 0x62, 0x91, 0x0f, 0x03, 0x2c, 0x30, 0x7f, 0xe1, 0xc5, 0x00, 0x07, 0x5a, 0x60, 0xfe, 0xc2, 0x8b, 0x01, +0xce, 0xb3, 0xc0, 0xfc, 0xc5, 0x07, 0x08, 0x06, 0x0d, 0x68, 0x00, 0x0a, 0x71, 0x50, 0x4c, 0x04, 0x00, 0x4c, 0x30, 0x14, 0x13, 0x10, 0x14, 0x62, 0xa2, 0x18, 0xd6, 0x04, 0x04, 0x10, 0x0c, 0xc5, 0x04, 0x04, 0xc1, 0x50, 0x08, 0x03, 0x1d, 0x01, 0xc1, 0x26, 0x8a, 0xa1, 0x10, 0x4d, 0x40, 0x50, 0x0c, 0x85, 0x38, 0x38, 0xa0, 0x52, 0x4c, 0x14, 0x10, 0x61, 0xe1, 0x1f, 0x71, 0xc0, 0x3b, 0xd6, 0x83, 0x32, 0xf8, 0x31, 0xae, 0xc0, 0x7c, 0xf1, 0x41, 0x06, 0x68, 0xf1, 0x41, 0x16, 0x8e, 0x2c, 0x32, 0x40, 0x0b, 0x57, 0x16, 0x98, 0x1f, 0x19, 0x60, 0x91, 0x17, 0xae, 0x0c, 0xf0, 0xa2, 0x05, 0xe6, 0x2f, 0x5c, 0x19, 0xe0, 0x41, 0x0b, 0xcc, 0x5f, 0x7c, 0x80, 0x85, 0x2b, 0x8b, 0x0c, 0xfe, 0x00, 0xbd, 0xc8, 0xc2, 0x91, 0x05, 0xe6, 0x03, 0xb4, 0x70, 0x65, 0x91, 0x23, 0x03, 0x2c, 0x30, 0x7f, 0xe1, 0xca, 0x00, 0x2f, 0x5a, 0x60, 0xfe, 0xc2, 0x95, 0x01, 0x1e, 0xb4, 0xc0, 0xfc, 0xc5, 0x07, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x90, 0x04, 0x00, 0x00, 0x00, 0xd6, 0x43, 0x33, 0xf8, 0x31, 0xae, 0xc0, 0x7c, 0xf1, 0x41, 0x06, 0x68, 0xf1, 0x41, 0x16, 0xce, 0x2c, 0x32, 0x40, 0x0b, 0x6f, 0x16, 0x98, 0x9f, 0x19, 0x60, 0x91, 0x17, 0xde, 0x0c, 0x70, 0xa4, 0x05, 0xe6, 0x2f, 0xbc, 0x19, 0xe0, 0x45, 0x0b, 0xcc, 0x5f, 0x7c, 0x80, 0x85, 0x37, 0x8b, 0x0c, 0xfe, 0x00, 0xbd, 0xc8, 0xc2, 0x99, 0x05, 0xe6, 0x03, 0xb4, 0xf0, 0x66, 0x91, 0x33, 0x03, 0x2c, 0x30, 0x7f, 0xe1, 0xcd, 0x00, 0x47, 0x5a, 0x60, 0xfe, 0xc2, 0x9b, 0x01, 0x5e, 0xb4, 0xc0, 0xfc, 0xc5, 0x07, 0x08, 0x06, 0x0d, 0x70, 0x00, 0x0a, 0x81, 0x50, 0x4c, 0x04, 0x00, 0x4c, 0x30, 0x14, 0x13, 0x10, +0x14, 0x82, 0xa2, 0x18, 0xe6, 0x04, 0x04, 0x10, 0x0c, 0xc5, 0x04, 0x04, 0xc1, 0x50, 0x08, 0x03, 0x21, 0x01, 0xc1, 0x28, 0x8a, 0xa1, 0x10, 0x4e, 0x40, 0x50, 0x0c, 0x85, 0x40, 0x38, 0xa0, 0x52, 0x50, 0x14, 0x10, 0x61, 0xe1, 0x1f, 0x71, 0xc0, 0x3b, 0xd6, 0x03, 0x33, 0xf8, 0x31, 0xae, 0xc0, 0x7c, 0xf1, 0x41, 0x06, 0x68, 0xf1, 0x41, 0x16, 0xae, 0x2c, 0x32, 0x40, 0x0b, 0x67, 0x16, 0x98, 0x5f, 0x19, 0x60, 0x91, 0x17, 0xce, 0x0c, 0xf0, 0xa3, 0x05, 0xe6, 0x2f, 0x9c, 0x19, 0xe0, 0x44, 0x0b, 0xcc, 0x5f, 0x7c, 0x80, 0x85, 0x33, 0x8b, 0x0c, 0xfe, 0x00, 0xbd, 0xc8, 0xc2, 0x95, 0x05, 0xe6, 0x03, 0xb4, 0x70, 0x66, 0x91, 0x2b, 0x03, 0x2c, 0x30, 0x7f, 0xe1, 0xcc, 0x00, 0x3f, 0x5a, 0x28, 0xf4, 0xc2, 0x99, 0x01, 0x7e, 0xb4, 0x50, 0xe2, 0x85, 0x03, 0x03, 0x04, 0xf1, 0x5e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x70, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6, 0x01, 0x4f, 0xa0, 0x80, 0x68, 0x7e, 0xf4, 0x90, 0x02, 0xfc, 0x81, 0xe8, 0x80, 0xe0, 0x88, 0xc6, 0x43, 0x0a, 0xf0, 0x07, 0xa2, 0x03, 0x82, 0x23, 0x1a, 0x0f, 0x29, 0xc0, 0x1f, 0x88, 0x0e, 0x08, 0x8e, 0x68, 0x3c, 0xa4, 0x00, 0x7f, 0x00, 0x1e, 0x01, 0x00, 0x0d, 0x84, 0x00, 0x00, 0x02, 0x22, 0x18, 0x0a, 0xa9, 0x50, 0x4c, 0x40, 0x50, 0x4a, 0x0b, 0xe3, 0x02, 0x8c, 0x30, 0x28, 0x60, 0x17, 0x7a, 0x48, 0x2d, 0x24, 0x04, 0x11, 0x00, 0x40, 0x30, 0x00, 0x01, 0x41, 0xb1, 0x01, 0xaa, 0x01, 0x41, 0xb1, 0x01, 0x6a, 0xb0, 0x01, 0x15, 0x1b, 0xa0, 0x06, 0x0c, 0x00, 0x75, 0x00, 0x46, 0x00, 0x00, 0xc2, 0x00, 0x00, 0xc2, 0x00, 0x00, 0xc2, 0x00, 0x00, +0x00, 0x1e, 0x01, 0x00, 0x0d, 0x74, 0x00, 0x00, 0x02, 0x22, 0x18, 0x0a, 0x89, 0x50, 0x4c, 0x40, 0x50, 0x4a, 0x0a, 0xe3, 0x02, 0x8c, 0x30, 0x28, 0x60, 0x17, 0x7a, 0x48, 0x2d, 0xa4, 0x03, 0x11, 0x00, 0x40, 0x30, 0x00, 0x01, 0x41, 0xb1, 0x01, 0xaa, 0x01, 0x41, 0xb1, 0x01, 0x6a, 0xb0, 0x01, 0x15, 0x1b, 0xa0, 0x06, 0x0c, 0x00, 0x1e, 0x01, 0x00, 0x0d, 0x68, 0x00, 0x00, 0x02, 0x22, 0x18, 0x0a, 0x71, 0x50, 0x4c, 0x40, 0x50, 0x8a, 0x09, 0xe3, 0x02, 0x8c, 0x30, 0x28, 0x60, 0x17, 0x7a, 0x48, 0x2d, 0x44, 0x03, 0x11, 0x00, 0x40, 0x30, 0x00, 0x01, 0x41, 0xb1, 0x01, 0xaa, 0x01, 0x41, 0xb1, 0x01, 0x6a, 0xb0, 0x01, 0x15, 0x1b, 0xa0, 0x06, 0x0c, 0x00, 0xd6, 0x01, 0x4f, 0xa0, 0x88, 0xc6, 0xc3, 0x9f, 0xe8, 0x98, 0x62, 0x8f, 0x7f, 0x64, 0x8a, 0x3d, 0xfe, 0x91, 0x29, 0xf6, 0xf8, 0x47, 0xa6, 0xd8, 0xe3, 0x1f, 0x99, 0x62, 0x8f, 0x7f, 0x64, 0x8a, 0x3d, 0xfe, 0x91, 0x29, 0xf6, 0xf8, 0xc7, 0x29, 0x1e, 0x01, 0x00, 0x0d, 0x74, 0x00, 0x00, 0x02, 0x22, 0x18, 0x0a, 0x89, 0x50, 0x4c, 0x40, 0x50, 0x4a, 0x0a, 0xe3, 0x02, 0x8c, 0x30, 0x28, 0x60, 0x17, 0x7a, 0x48, 0x2d, 0xa4, 0x03, 0x11, 0x00, 0x40, 0x30, 0x00, 0x01, 0x41, 0xb1, 0x01, 0xaa, 0x01, 0x41, 0x31, 0x14, 0x12, 0xe1, 0x80, 0x4a, 0x49, 0x51, 0x40, 0x84, 0x05, 0xfe, 0x44, 0x1c, 0xf0, 0x0e, 0xd6, 0x43, 0x2e, 0xf8, 0x31, 0x2e, 0xc0, 0xc2, 0x8b, 0x17, 0x98, 0x0f, 0xb0, 0x70, 0xe4, 0x6f, 0xf1, 0x02, 0xf3, 0x01, 0x16, 0x7e, 0xfc, 0x5f, 0xe4, 0x05, 0xe6, 0x03, 0x2c, 0xfc, 0x78, 0x91, 0x01, 0x16, 0x5e, 0xdc, 0xe1, 0xe6, 0xfe, 0x02, 0xf3, 0x01, 0x16, 0x8e, 0xfc, 0x2d, 0x5e, 0x60, 0x3e, 0xc0, 0xc2, +0x8f, 0xff, 0x8b, 0xbc, 0xc0, 0x7c, 0x80, 0x85, 0x2b, 0x7f, 0x99, 0x17, 0x98, 0x2f, 0x1e, 0xd6, 0x43, 0x33, 0xf8, 0x31, 0x2e, 0x40, 0x55, 0x2d, 0x9c, 0x79, 0x81, 0xf9, 0x00, 0x0b, 0x6f, 0x5e, 0x64, 0x80, 0xaa, 0x85, 0x33, 0x2f, 0x30, 0x1f, 0xa0, 0xaa, 0x6a, 0xe1, 0xcd, 0xff, 0xdf, 0xe7, 0x05, 0xe6, 0x03, 0x2c, 0xbc, 0x79, 0x91, 0x01, 0xaa, 0x16, 0xae, 0xfc, 0xff, 0xff, 0x01, 0xd6, 0x03, 0x33, 0xf8, 0x31, 0x2e, 0x40, 0x55, 0x55, 0x55, 0x2d, 0x30, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xd6, 0x43, 0x34, 0xf8, 0x31, 0x2e, 0x40, 0x55, 0x2d, 0xfc, 0x79, 0x81, 0xf9, 0x00, 0x0b, 0x8f, 0x5e, 0x64, 0x80, 0xaa, 0x85, 0x3f, 0x2f, 0x30, 0x1f, 0xa0, 0xaa, 0x6a, 0xe1, 0xd1, 0xff, 0xdf, 0xe9, 0x05, 0xe6, 0x03, 0x2c, 0x3c, 0x7a, 0x91, 0x01, 0xaa, 0x16, 0xfe, 0xfc, 0xff, 0xff, 0x01, 0xd6, 0x43, 0x34, 0xf8, 0x31, 0x2e, 0x40, 0x55, 0x55, 0x55, 0x2d, 0x30, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xd6, 0x02, 0x35, 0xc0, 0xb8, 0x00, 0x55, 0xb5, 0xd0, 0xe9, 0x5f, 0x6c, 0x80, 0x05, 0xe6, 0xff, 0xff, 0x2f, 0x34, 0x1a, 0xa0, 0x16, 0xf9, 0x17, 0x3a, 0x0d, 0x50, 0xa7, 0xaa, 0xc5, 0xfe, 0x85, 0x46, 0x03, 0x54, 0xd5, 0x02, 0xf3, 0x7f, 0xa1, 0xcf, 0x00, 0x55, 0x15, 0xd6, 0x42, 0x36, 0xc0, 0xb8, 0x00, 0x55, 0xb5, 0x50, 0xec, 0x5f, 0x6c, 0x80, 0x5a, 0x60, 0xfe, 0xff, 0x2f, 0xd4, 0x1a, 0xa0, 0x16, 0xf9, 0x17, 0x9a, 0x0d, 0x50, 0xdf, 0xaa, 0xc5, 0xfe, 0x85, 0x62, 0x03, 0xd4, 0x02, 0xf3, 0x7f, 0xa1, 0xd6, 0x00, 0x55, 0x55, 0x15, 0xd6, 0x02, 0x36, 0xc0, 0xb8, 0x00, 0x55, +0x55, 0x55, 0xb5, 0xc0, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5f, 0x28, 0x33, 0x42, 0x60, 0x68, 0xc0, 0x85, 0x4e, 0x17, 0xbb, 0xdc, 0xfd, 0x0e, 0xd6, 0x01, 0x4f, 0xf0, 0x80, 0xe0, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xf5, 0x80, 0x27, 0x78, 0x80, 0x70, 0x80, 0x70, 0x80, 0x70, 0x80, 0x70, 0x80, 0x70, 0x80, 0x70, 0x80, 0x70, 0x00, 0xc5, 0x01, 0x0d, 0x60, 0x88, 0xc3, 0x98, 0x04, 0x00, 0x63, 0x84, 0x31, 0x26, 0x10, 0x43, 0x4c, 0xc6, 0x18, 0xa2, 0x09, 0x04, 0x61, 0x8c, 0x09, 0x44, 0x18, 0x43, 0x18, 0x86, 0x38, 0x02, 0x31, 0xc4, 0x64, 0x8c, 0x21, 0x9a, 0x40, 0x8c, 0x31, 0xc4, 0x61, 0x4c, 0xa0, 0x30, 0x08, 0x04, 0x75, 0x00, 0x46, 0x00, 0x00, 0xc2, 0x00, 0x00, 0xc2, 0x00, 0x00, 0xc2, 0x00, 0xc2, 0x00, 0x00, 0x06, 0x0d, 0x74, 0x00, 0x0a, 0x89, 0x40, 0x04, 0x00, 0x14, 0x43, 0x30, 0x10, 0x10, 0xc0, 0x14, 0x33, 0x80, 0x14, 0x92, 0x0a, 0x08, 0xee, 0x30, 0x03, 0x04, 0x38, 0xc2, 0x1d, 0x01, 0xca, 0x01, 0x03, 0x82, 0x09, 0x28, 0x1c, 0x40, 0x20, 0x04, 0x0a, 0x08, 0xb0, 0x50, 0xce, 0x0f, 0xd6, 0x43, 0x33, 0xf8, 0x31, 0xae, 0xc0, 0x7c, 0xf1, 0x41, 0x06, 0x68, 0xf1, 0x41, 0x16, 0xce, 0x2c, 0x32, 0x40, 0x0b, 0x6f, 0x16, 0x98, 0x9f, 0x19, 0x60, 0x91, 0x17, 0xde, 0x0c, 0x70, 0xa4, 0x05, 0xe6, 0x2f, 0xbc, 0x19, 0xe0, 0x45, 0x0b, 0xcc, 0x5f, 0x7c, 0x80, 0x85, 0x37, 0x8b, 0x0c, 0xfe, 0x00, 0xbd, 0xc8, 0xc2, 0x99, 0x05, 0xe6, 0x03, 0xb4, 0xf0, 0x66, 0x91, 0x33, 0x03, 0x2c, 0x30, 0x7f, 0xe1, 0xcd, 0x00, 0x47, 0x5a, 0x60, 0xfe, 0xc2, 0x9b, 0x01, 0x5e, 0xb4, 0xc0, 0xfc, +0x0f, 0xf1, 0x78, 0x02, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x42, 0x05, 0xc8, 0xa0, 0x00, 0x35, 0xc2, 0xa0, 0x80, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x11, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x43, 0x34, 0xf0, 0xc0, 0xb8, 0x00, 0x55, 0x55, 0x23, 0x0c, 0x0a, 0x58, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0xde, 0x43, 0x0b, 0x40, 0xc0, 0xb8, 0x00, 0x55, 0x55, 0x23, 0x0c, 0x0a, 0x58, 0x55, 0x55, 0x55, 0x55, 0x03, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0xde, 0x03, 0x0c, 0x40, 0xc0, 0xb8, 0x00, 0x55, 0x55, 0x23, 0x0c, 0x0a, 0x58, 0x55, 0x55, 0x55, 0x55, 0x03, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0xde, 0x03, 0x0d, 0x40, 0xc0, 0xb8, 0x00, 0x55, 0x55, 0x23, 0x0c, 0x0a, 0x58, 0x55, 0x55, 0x55, 0x55, 0x03, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0xc6, 0x7f, 0x7d, 0x7f, 0x7c, 0x7f, 0x7f, 0x7f, 0x7d, 0x7f, 0x7e, 0x7f, 0x7e, 0x7f, 0x7e, 0x7f, 0x7e, 0x7f, 0x7e, 0x7f, 0x7d, 0x7f, 0x7d, 0x7f, 0x7c, 0x7f, 0x7b, 0x7f, 0x78, 0x7f, 0x71, 0x7f, 0xfb, 0x80, 0x8f, 0x80, 0x88, 0x80, 0x85, 0x80, 0x84, 0x80, 0x83, 0x80, 0x83, 0x80, 0x82, 0x80, 0x82, 0x80, 0x82, 0x80, 0x82, 0x80, 0x82, 0x80, 0x82, 0x80, 0x82, 0x80, 0x84, 0x80, 0x91, 0xfb, 0xfb, 0x01, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x39, 0x41, 0x49, 0x51, 0x59, 0x61, 0x69, 0x71, 0x7a, 0x7f, 0x77, 0x6f, 0x67, 0x5f, 0x57, 0x4f, 0x47, 0x3f, 0x36, 0x2e, 0x26, 0x1e, 0x16, 0x0e, 0x06, 0xfe, 0xf6, 0xee, +0xe6, 0xde, 0xd5, 0xcd, 0xc5, 0xbd, 0xb5, 0xad, 0xa5, 0x9d, 0x95, 0x8d, 0x84, 0x83, 0x8b, 0x93, 0x9b, 0xa3, 0xab, 0xb3, 0xbc, 0xc3, 0xcc, 0xd4, 0xdc, 0xe4, 0xec, 0xf4, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0x10, 0x30, 0x10, 0xc8, 0xd0, 0x00, 0x28, 0x20, 0xf8, 0xe8, 0xf0, 0x00, 0x10, 0x10, 0xf8, 0xe0, 0x08, 0x30, 0x10, 0xe0, 0xd8, 0xf8, 0x08, 0x08, 0x28, 0x20, 0xd0, 0xc0, 0x00, 0x28, 0x18, 0xf8, 0xe8, 0xf8, 0x08, 0x10, 0x10, 0xf8, 0xd8, 0xe0, 0x10, 0x40, 0x10, 0xd0, 0xd0, 0x00, 0x28, 0x18, 0xf0, 0xd8, 0xf8, 0x28, 0x20, 0xe0, 0xd0, 0xf0, 0x10, 0x10, 0xf0, 0xf0, 0x28, 0x18, 0xd0, 0xc8, 0xf0, 0x28, 0x30, 0xf0, 0xc8, 0xe0, 0x08, 0x10, 0x18, 0x18, 0xf8, 0xd0, 0xe0, 0x08, 0x20, 0x10, 0xf0, 0xf0, 0x00, 0x10, 0xf0, 0xe0, 0xe0, 0x28, 0x30, 0xd0, 0x00, 0x18, 0xd8, 0xf0, 0x30, 0x00, 0xd0, 0xf8, 0x18, 0x20, 0xf8, 0xd0, 0xf0, 0x38, 0x10, 0xd0, 0xf0, 0xf8, 0xf0, 0xf0, 0x18, 0x58, 0xe8, 0x98, 0xf8, 0x00, 0x08, 0x40, 0x08, 0xc8, 0xd0, 0xf8, 0x10, 0x18, 0xf8, 0x00, 0xf8, 0x10, 0xe8, 0xb8, 0x00, 0x08, 0x38, 0x30, 0xa8, 0xa8, 0xf0, 0x38, 0x40, 0xf0, 0xc0, 0xc8, 0xf8, 0x18, 0x10, 0xf8, 0x28, 0x10, 0x90, 0xe8, 0x40, 0x28, 0x08, 0xb0, 0xc0, 0x10, 0x38, 0x10, 0xd8, 0xe8, 0xf8, 0xf0, 0xf8, 0x18, 0x40, 0x00, 0xa0, 0xd0, 0x20, 0x10, 0x10, 0x20, 0xf0, 0xc0, 0xd0, 0x10, 0x38, 0xf8, 0xd0, 0xf8, 0x28, 0x20, 0xd8, +0xd0, 0x08, 0x28, 0xf0, 0xf8, 0x10, 0xf8, 0xe8, 0xf0, 0x08, 0x00, 0x00, 0x00, 0xf8, 0xf0, 0x08, 0xf8, 0xf0, 0x00, 0xe8, 0x28, 0x10, 0xd8, 0x10, 0xf8, 0xd0, 0xf0, 0x08, 0x30, 0x30, 0xd8, 0xd0, 0xe8, 0xf0, 0xf0, 0x18, 0x20, 0xe0, 0x08, 0xf0, 0xa8, 0xf0, 0x60, 0x18, 0xd0, 0xe0, 0xc8, 0xe8, 0x30, 0x30, 0xe8, 0xf8, 0xe0, 0xf0, 0x08, 0xd0, 0x20, 0x28, 0xd0, 0xd8, 0x18, 0xe0, 0xd8, 0x28, 0x28, 0xf0, 0xc0, 0x20, 0x00, 0xd0, 0x18, 0x08, 0xf8, 0xe8, 0x20, 0x08, 0xb0, 0xf0, 0x28, 0x28, 0x00, 0xb0, 0xd0, 0x38, 0x40, 0xf8, 0xc0, 0xe0, 0x08, 0x20, 0x20, 0x08, 0xf0, 0xe0, 0xf0, 0x00, 0xf8, 0x20, 0x30, 0xe0, 0xc8, 0xf0, 0x08, 0x00, 0x10, 0x28, 0x00, 0xc0, 0xd0, 0x08, 0x10, 0x28, 0x10, 0xc8, 0xc8, 0x10, 0x30, 0xd0, 0xf8, 0x48, 0xf0, 0xb0, 0xd8, 0x10, 0x50, 0xf8, 0xc8, 0x38, 0xe8, 0xf0, 0x18, 0xc0, 0x08, 0x30, 0x08, 0xd8, 0xe0, 0x18, 0xf8, 0xf8, 0x28, 0xf0, 0xc0, 0x30, 0xf8, 0xb0, 0x20, 0x30, 0xf0, 0xc0, 0x10, 0x18, 0xc0, 0x00, 0x38, 0x18, 0xc0, 0xc0, 0x08, 0x18, 0x18, 0x10, 0xd0, 0xa8, 0xf0, 0x48, 0x50, 0xd8, 0xb8, 0xe8, 0x08, 0x08, 0xf8, 0x30, 0x00, 0xd0, 0x00, 0x00, 0xf0, 0x08, 0x00, 0xf0, 0x00, 0x00, 0x18, 0xd8, 0xd0, 0x18, 0x28, 0x08, 0xc0, 0x00, 0x18, 0xe8, 0x10, 0xf8, 0xb8, 0x10, 0x50, 0xc0, 0xd0, 0x38, 0x20, 0xe0, 0xb0, 0x20, 0x28, 0xd0, 0x30, 0x18, 0xa8, 0xc8, 0x20, 0x40, 0xf8, 0xf8, 0x08, 0xe0, 0xc8, 0x08, 0x48, 0x20, 0xd8, 0xc8, 0xe0, 0x08, 0x50, 0x18, 0xd0, 0xf8, 0xf0, 0xe8, 0xf8, 0x30, 0x10, 0xd8, 0x10, 0x00, 0xd8, 0xd8, 0x10, 0x48, 0xf8, 0xd8, 0x00, 0xd0, 0xf8, 0x58, 0xe0, 0xc0, 0x20, 0xf0, 0xf0, 0x28, 0xe0, 0xd0, +0x08, 0x18, 0x18, 0x00, 0xd8, 0xb8, 0x10, 0x48, 0x10, 0xe0, 0xc0, 0xe8, 0x20, 0x10, 0x08, 0x18, 0xf0, 0xb0, 0xf0, 0x58, 0x18, 0xc8, 0xe8, 0xf8, 0x00, 0xf0, 0x10, 0x28, 0xf8, 0xd0, 0xd0, 0x18, 0x38, 0xf0, 0xc0, 0x18, 0x18, 0xe8, 0x20, 0xf0, 0xd8, 0x18, 0x00, 0xe8, 0xf0, 0x20, 0x20, 0xe0, 0xe8, 0xe8, 0xf8, 0x18, 0x08, 0xe8, 0x08, 0x00, 0xf0, 0x20, 0xd8, 0xd8, 0x10, 0x00, 0x20, 0x08, 0xc0, 0xe0, 0x18, 0x18, 0x18, 0xf0, 0xc8, 0xd8, 0x18, 0x50, 0x00, 0xc0, 0xe0, 0x00, 0x18, 0x08, 0x10, 0x18, 0xe8, 0xd8, 0x00, 0x10, 0xf0, 0xf0, 0x38, 0x00, 0xc0, 0x18, 0x10, 0xf0, 0x28, 0xe8, 0xd0, 0x20, 0x08, 0xe8, 0x00, 0x28, 0x00, 0xc0, 0xf0, 0x08, 0x30, 0x18, 0xd0, 0xd8, 0xe8, 0x08, 0x30, 0x30, 0xd8, 0x90, 0x08, 0x50, 0x08, 0xd8, 0xd0, 0x00, 0x20, 0x00, 0xe0, 0x00, 0x28, 0x08, 0xe0, 0xe8, 0xf8, 0x08, 0x18, 0x18, 0xf8, 0xd8, 0xe8, 0x20, 0x10, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x08, 0xf0, 0xe8, 0xe8, 0x28, 0x38, 0xe0, 0xe0, 0xe0, 0xd0, 0x40, 0x38, 0xd8, 0xe0, 0xd8, 0x08, 0x20, 0xe0, 0xf8, 0x30, 0xf8, 0xc0, 0x00, 0x28, 0xf8, 0xf0, 0x08, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0x28, 0x20, 0xd8, 0xd8, 0xf8, 0x08, 0x08, 0xf8, 0xf8, 0x10, 0x08, 0xd8, 0xe0, 0x08, 0x30, 0x28, 0xd8, 0xb8, 0xf8, 0x08, 0x20, 0x18, 0xd0, 0xf8, 0xf8, 0x00, 0x00, 0xe8, 0x18, 0x18, 0xf8, 0xd8, 0xe8, 0x10, 0x20, 0xf8, 0xc0, 0xe8, 0x28, 0x30, 0xe8, 0xd8, 0xe0, 0xf8, 0x10, 0x30, 0x10, 0xb8, 0xe0, 0x08, 0x08, 0x28, 0x10, 0xd8, 0xd0, 0xe8, 0x08, 0x18, 0x28, 0x00, 0xc8, 0xe0, 0x10, 0x30, 0x08, 0xd0, 0xf0, 0xf8, 0x00, 0x18, 0x08, 0x18, 0xe0, 0xb0, 0x10, 0x30, 0x10, 0xf8, +0xd0, 0xe8, 0x08, 0x00, 0x00, 0x20, 0x10, 0xd8, 0xe8, 0x18, 0xe0, 0xf0, 0x20, 0xf8, 0xe8, 0xf8, 0x00, 0x08, 0x08, 0xf0, 0xf0, 0xf8, 0x20, 0x10, 0xb8, 0x00, 0x30, 0x18, 0xe8, 0xc8, 0x00, 0x10, 0xf0, 0x00, 0x08, 0xf0, 0xf8, 0x10, 0x00, 0xe8, 0xf8, 0x00, 0x08, 0xf8, 0xe0, 0xf8, 0x18, 0x20, 0xd0, 0xd8, 0x10, 0x00, 0xf8, 0x10, 0x00, 0xe0, 0x00, 0xf8, 0xf0, 0x28, 0x08, 0xd8, 0x00, 0x00, 0xe8, 0xf8, 0x20, 0x20, 0xd0, 0xe0, 0x10, 0x08, 0xf8, 0xe8, 0xf0, 0x10, 0x10, 0x00, 0xf8, 0xc8, 0xf8, 0x10, 0x10, 0x10, 0x08, 0xd8, 0xc0, 0x20, 0x28, 0xf8, 0xe0, 0x08, 0x08, 0xe0, 0x00, 0x08, 0x08, 0xf0, 0xe8, 0x00, 0xf0, 0x08, 0x30, 0xf0, 0xb8, 0xe0, 0x10, 0x28, 0x20, 0xe8, 0xc0, 0xe8, 0x20, 0x28, 0xf0, 0xe8, 0x10, 0xf0, 0xf0, 0xf0, 0x08, 0x30, 0x08, 0xc8, 0xe0, 0x10, 0x08, 0x08, 0x18, 0x08, 0xe0, 0xf0, 0x00, 0x08, 0xf0, 0xe8, 0x30, 0x20, 0xd0, 0xd0, 0x08, 0x18, 0x00, 0x08, 0x18, 0x00, 0xf0, 0xf8, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0xf0, 0x00, 0x08, 0x08, 0x00, 0xe8, 0xf0, 0x10, 0x10, 0xe8, 0x10, 0x10, 0xe0, 0xd8, 0x20, 0x48, 0xe0, 0xd8, 0xe8, 0x00, 0x28, 0xf0, 0xf0, 0x28, 0x00, 0xd0, 0xe0, 0x18, 0x28, 0xf8, 0xe8, 0x00, 0xf8, 0xf8, 0x28, 0xf0, 0xc0, 0x20, 0x18, 0xf0, 0x00, 0xe0, 0xe8, 0x18, 0x18, 0x00, 0xe8, 0xf0, 0x00, 0xf8, 0xf8, 0xf8, 0x10, 0x40, 0xe8, 0xa0, 0x00, 0x28, 0x00, 0xf8, 0x00, 0xf0, 0xd8, 0x00, 0x20, 0x20, 0xf0, 0xc0, 0x10, 0x18, 0xd8, 0x10, 0x28, 0xe8, 0xc8, 0xf0, 0x28, 0x20, 0xf0, 0xe0, 0xe8, 0x00, 0x10, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0x00, 0x00, 0xf8, 0x00, 0x18, 0x00, 0xd0, 0xe0, 0x18, 0x28, 0x10, 0xd0, 0xd8, 0x00, +0x08, 0x00, 0x08, 0x10, 0xf8, 0xd0, 0xf0, 0x38, 0x00, 0xc8, 0x10, 0x28, 0x00, 0xe0, 0xe8, 0x00, 0x08, 0x08, 0xf8, 0xf8, 0xf0, 0x00, 0x18, 0xf8, 0xf0, 0x10, 0xf0, 0xe8, 0x08, 0x10, 0xf0, 0xf8, 0x10, 0xf0, 0xf0, 0xf8, 0x20, 0x08, 0xd0, 0x00, 0x00, 0xf8, 0x00, 0xf8, 0x20, 0x00, 0xe0, 0xd8, 0x00, 0x28, 0xf0, 0xf8, 0x10, 0xe8, 0xe8, 0xf8, 0x28, 0x00, 0xe0, 0xf8, 0x00, 0x10, 0xe8, 0xe0, 0xf8, 0x10, 0x18, 0xf0, 0xf0, 0xf0, 0xf0, 0xf8, 0xf8, 0x08, 0x18, 0xf0, 0xe8, 0xe8, 0xf0, 0x10, 0xf0, 0x08, 0x08, 0xd0, 0x10, 0x20, 0xe8, 0xd8, 0xf0, 0x18, 0xf8, 0x00, 0x18, 0xf0, 0xd8, 0xe8, 0x00, 0x20, 0x00, 0x00, 0x00, 0xd0, 0xe8, 0x08, 0x18, 0x10, 0xe8, 0xd8, 0xf0, 0x00, 0x10, 0x18, 0x00, 0xe0, 0xe8, 0x00, 0x10, 0x08, 0xf8, 0xf0, 0xf8, 0x08, 0xf8, 0xe8, 0xf0, 0x10, 0x18, 0x00, 0xe0, 0xf0, 0x20, 0xf8, 0xe0, 0x00, 0x10, 0x08, 0xf0, 0xf8, 0xf8, 0xe8, 0x00, 0x10, 0x00, 0xf8, 0xf8, 0xf0, 0xf8, 0xf8, 0xf8, 0x18, 0x08, 0xf8, 0xe8, 0xe0, 0x00, 0x08, 0x18, 0x00, 0xd8, 0xf8, 0x18, 0xe8, 0xe8, 0x10, 0xf8, 0xf8, 0x00, 0xf8, 0xf0, 0xf8, 0x18, 0x08, 0xf0, 0xf8, 0xe0, 0xf0, 0x20, 0x30, 0xf0, 0xc0, 0xf0, 0x10, 0x10, 0x08, 0x08, 0x08, 0xd0, 0xe8, 0x18, 0x08, 0xf0, 0xf8, 0x00, 0x00, 0x00, 0xf0, 0xf8, 0x08, 0xf8, 0xe8, 0x00, 0x10, 0x08, 0xe0, 0xe0, 0x00, 0x08, 0x08, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x08, 0xf8, 0xf0, 0x00, 0x00, 0x08, 0x10, 0xe8, 0xe8, 0x00, 0xf0, 0xf8, 0x18, 0x18, 0xe0, 0xe8, 0x08, 0xf8, 0xf8, 0xf0, 0x10, 0x00, 0xf0, 0x18, 0x08, 0xe8, 0xe8, 0x10, 0xf8, 0xe8, 0x00, 0xf8, 0x10, 0x08, 0xe0, 0xd8, 0x00, 0x10, 0x10, 0x00, 0xe0, +0xe0, 0x00, 0x18, 0x18, 0xf0, 0xd8, 0xf8, 0xf8, 0x00, 0x18, 0x00, 0xd8, 0xe8, 0x10, 0x08, 0xf8, 0x10, 0xf0, 0xe8, 0xf8, 0x00, 0x08, 0x10, 0x00, 0xe0, 0xf8, 0x08, 0xf8, 0xf8, 0xe8, 0xf0, 0x10, 0x10, 0xe8, 0xf8, 0xf8, 0xe8, 0xf8, 0x10, 0x10, 0xf8, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf0, 0xf0, 0xf8, 0x08, 0x08, 0x00, 0xf0, 0xe0, 0xf0, 0x08, 0xf8, 0x10, 0x10, 0xe8, 0xe0, 0xf0, 0x10, 0xf8, 0xf0, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0x00, 0x20, 0x00, 0xd8, 0xf0, 0xf8, 0x00, 0x28, 0x18, 0xc8, 0xe0, 0x00, 0x08, 0x18, 0xe8, 0xe0, 0x00, 0x00, 0x08, 0x10, 0xe8, 0xe0, 0x08, 0x00, 0xf0, 0x00, 0x08, 0xf0, 0xf0, 0xf8, 0xf0, 0x08, 0x18, 0xf0, 0xd8, 0xf0, 0x00, 0x10, 0x18, 0xf0, 0xd8, 0xf0, 0x00, 0x18, 0x10, 0xe0, 0xd8, 0xf0, 0x08, 0x18, 0x18, 0xf0, 0xd8, 0xe8, 0x00, 0x00, 0x08, 0x10, 0xf8, 0xe0, 0xe8, 0x00, 0x08, 0x10, 0xf8, 0xf0, 0xf8, 0xe8, 0xf8, 0x00, 0x08, 0x08, 0xe8, 0xf8, 0xf8, 0xe8, 0x08, 0x00, 0xf8, 0x00, 0xf8, 0xf0, 0x00, 0x10, 0x08, 0xf0, 0xe0, 0xf8, 0x08, 0x08, 0x10, 0x00, 0xe8, 0xf8, 0xf0, 0xf0, 0x10, 0x20, 0xf8, 0xe0, 0xe8, 0x00, 0x10, 0x18, 0x08, 0xe0, 0xe8, 0x00, 0x08, 0x00, 0x10, 0x18, 0xf0, 0xe0, 0xf0, 0x00, 0x00, 0x08, 0x20, 0x08, 0xe8, 0xe8, 0xf0, 0x10, 0x18, 0xf8, 0x00, 0x08, 0xe8, 0xf8, 0x08, 0x00, 0x18, 0x08, 0xe0, 0xf8, 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x08, 0x00, 0x08, 0xf0, 0xf0, 0x00, 0x08, 0x28, 0x10, 0xd0, 0xe8, 0x18, 0x20, 0xf8, 0xe0, 0x00, 0x08, 0xf8, 0x08, 0x18, 0x08, 0xe8, 0xe8, 0x08, 0x10, 0x10, 0x00, 0xf0, 0x00, 0x00, 0xf8, 0x18, 0x10, 0xf0, 0xf8, 0x08, 0x00, 0xf8, 0x00, +0x08, 0x08, 0x08, 0x00, 0xe8, 0xf8, 0x18, 0x18, 0x00, 0xf0, 0xe8, 0xf8, 0x18, 0x18, 0x00, 0xe8, 0x08, 0x08, 0xf0, 0x08, 0x08, 0x00, 0x00, 0x08, 0x10, 0xf0, 0xe8, 0x08, 0x20, 0x10, 0xe8, 0xe8, 0x08, 0x08, 0x08, 0x10, 0xf8, 0xf0, 0x08, 0xf8, 0x00, 0x10, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x18, 0x08, 0xe0, 0xe8, 0x08, 0x30, 0x08, 0xe8, 0xe8, 0x00, 0x10, 0xf8, 0x10, 0x10, 0xf8, 0xe0, 0xf0, 0x18, 0x10, 0x00, 0x00, 0xf0, 0xf8, 0x10, 0x08, 0x00, 0xf8, 0xf8, 0x08, 0x20, 0xf8, 0xe8, 0x00, 0x00, 0x08, 0x10, 0x10, 0xf0, 0xf0, 0xf8, 0x00, 0x10, 0x08, 0x10, 0x00, 0xe0, 0xf8, 0x10, 0x08, 0x10, 0x00, 0xe8, 0x00, 0x18, 0x08, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf0, 0xe8, 0x10, 0x00, 0x08, 0x08, 0xe8, 0xe8, 0x00, 0x10, 0x08, 0x08, 0xf8, 0xe8, 0xf8, 0x08, 0x00, 0x00, 0x10, 0x00, 0xe8, 0xe8, 0x10, 0x18, 0xf0, 0xe0, 0x00, 0x08, 0x10, 0x00, 0xe8, 0xf0, 0x00, 0x00, 0x08, 0x18, 0x00, 0xd8, 0xf0, 0x08, 0x08, 0x08, 0xf8, 0xe8, 0xf8, 0x00, 0x08, 0x08, 0xf8, 0xf0, 0xf0, 0x00, 0xf8, 0xf8, 0x08, 0x08, 0xe8, 0xe0, 0x08, 0x00, 0x00, 0x00, 0xe8, 0xe0, 0x08, 0x20, 0xf8, 0xe8, 0xe8, 0xf0, 0x00, 0x10, 0x08, 0xe0, 0xe0, 0x08, 0x18, 0x00, 0xe8, 0xe8, 0xf0, 0x00, 0x10, 0x00, 0xf0, 0xe8, 0xe8, 0xf8, 0x08, 0x10, 0x00, 0xd8, 0xe8, 0x00, 0x00, 0xf8, 0x10, 0x00, 0xe0, 0xe8, 0xf8, 0x08, 0x10, 0x00, 0xf8, 0xe8, 0xe8, 0x08, 0x00, 0xf0, 0x00, 0x10, 0x00, 0xe8, 0xe8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf0, 0xf0, 0xf8, 0xf8, 0xf0, 0x08, 0x08, 0xf8, 0xe8, 0xf0, 0xf8, 0x08, 0xf8, 0xf0, 0x00, 0x08, 0xf8, 0xe0, 0xf0, 0x08, 0x00, 0xf0, 0xf8, +0xf0, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf0, 0xf8, 0xf8, 0xf0, 0xf0, 0x08, 0x08, 0xf0, 0xe8, 0xf0, 0xf8, 0xf8, 0xf0, 0x08, 0x00, 0xf0, 0xf0, 0xe8, 0xf8, 0x08, 0x08, 0xf8, 0xf0, 0xf0, 0xe8, 0xf8, 0x08, 0xf8, 0xf0, 0xf8, 0x00, 0xf0, 0xe8, 0xf8, 0x08, 0x08, 0xf0, 0xd8, 0xf0, 0x00, 0x00, 0x08, 0xf8, 0xe0, 0xf0, 0x00, 0xf0, 0x00, 0x08, 0xf0, 0xe8, 0xf8, 0x00, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0x00, 0x00, 0xf0, 0xf0, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xe8, 0xf0, 0x00, 0x00, 0x08, 0x00, 0xe8, 0xe8, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf0, 0xf0, 0xf8, 0x00, 0xf8, 0x00, 0x00, 0xf0, 0xf0, 0xf8, 0x00, 0xf0, 0xf0, 0x08, 0x00, 0xf0, 0xf0, 0x00, 0xf0, 0xf8, 0x00, 0x00, 0xf8, 0xf0, 0xf0, 0xf8, 0x08, 0xf8, 0xf0, 0xe8, 0xf8, 0x00, 0xf8, 0x08, 0x00, 0xe0, 0xe8, 0x08, 0x08, 0xf8, 0x00, 0x00, 0xe8, 0xf0, 0x08, 0xf8, 0x00, 0x08, 0xf8, 0xe0, 0xf0, 0x08, 0x08, 0xf8, 0xe8, 0xf0, 0x08, 0x00, 0xf8, 0x00, 0x00, 0xf8, 0xf0, 0xf8, 0x08, 0xf8, 0xf0, 0xf8, 0xf8, 0x08, 0x08, 0xf0, 0xe8, 0xf8, 0x00, 0x10, 0x00, 0xf0, 0xf0, 0xf8, 0x00, 0x08, 0x00, 0xf8, 0xf0, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 0x08, 0xf8, 0xe8, 0xf0, 0x10, 0x18, 0xf0, 0xe0, 0xf0, 0x08, 0x10, 0x08, 0xf0, 0xe8, 0xf8, 0x00, 0x08, 0x10, 0xf8, 0xf0, 0xf0, 0x00, 0x00, 0xf8, 0x00, 0x10, 0x08, 0xe0, 0xf8, 0x08, 0xf8, 0x08, 0x10, 0xf8, 0xe8, 0xf8, 0x10, 0x00, 0xf8, 0xf8, 0xf8, 0x08, 0x18, 0xf8, 0xf0, 0x00, 0x08, 0x08, 0xf8, 0xf8, 0xf8, 0x00, 0x10, 0x08, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x08, 0x08, 0xf8, 0xf0, 0xf0, 0x08, 0x10, 0x08, 0xf8, 0xf0, +0xf8, 0x08, 0x08, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0x10, 0x10, 0xf8, 0xe8, 0xf0, 0x10, 0x10, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x08, 0x08, 0x00, 0x00, 0xf8, 0xf8, 0x08, 0x08, 0x00, 0x00, 0xf8, 0xf0, 0x00, 0x08, 0x10, 0x00, 0xf0, 0xf8, 0x08, 0xf8, 0x08, 0x10, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x08, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0x08, 0x18, 0x00, 0xf0, 0xf8, 0x00, 0x08, 0x00, 0x08, 0x10, 0x00, 0xf0, 0xf8, 0x00, 0x08, 0x10, 0x08, 0xf8, 0xf0, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0xf8, 0xf8, 0x00, 0x08, 0x10, 0x08, 0xf8, 0xf0, 0xf8, 0x08, 0x08, 0xf8, 0x08, 0x10, 0x00, 0xf8, 0xf0, 0x00, 0x08, 0x00, 0x08, 0x08, 0xf0, 0xf0, 0x00, 0x08, 0x08, 0x08, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x08, 0x08, 0x00, 0xf8, 0xf8, 0x00, 0x08, 0x00, 0xf8, 0xf8, 0x08, 0x08, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x08, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x08, 0x00, 0xf8, 0xf8, 0xf0, 0x00, 0x00, 0x00, 0x08, 0x08, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x08, 0x00, 0xf0, 0xf8, 0x08, 0x10, 0x08, 0xf0, 0xf0, 0x00, 0x08, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x08, 0x10, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x08, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x08, 0x08, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x08, 0x08, 0xf8, 0xf0, 0xf8, 0x00, 0xf8, 0xf8, 0x08, 0x08, 0xf0, 0xf0, 0xf8, 0x08, 0x08, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x08, 0xf8, 0xf0, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf0, 0xf8, 0x00, 0x00, +0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x08, 0x08, 0xf0, 0xf0, 0x00, 0x00, 0x08, 0xf8, 0xf8, 0xf8, 0x00, 0x08, 0x00, 0xf8, 0xf0, 0xf8, 0x00, 0xf8, 0x00, 0x08, 0x00, 0xf0, 0xf0, 0xf8, 0x00, 0x08, 0x08, 0xf8, 0xf0, 0xf0, 0xf8, 0x00, 0x00, 0x08, 0xf8, 0xf0, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf0, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x08, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x08, 0xf8, 0xf0, 0xf8, 0x00, 0x08, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x08, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x08, 0xf8, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x08, 0xf8, 0xf0, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf0, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf0, 0x00, 0x08, 0xf8, 0xf0, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf0, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf0, 0xf8, 0x00, 0xf8, +0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf0, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0xf0, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf0, 0xf0, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf0, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf0, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x08, 0x00, 0xf0, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf0, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0x00, +0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, +0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xf8, 0xf8, +0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, +0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xfa, 0xfa, 0xfa, 0xfa, 0xf7, 0xfa, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0c, 0x09, 0x09, 0x09, 0x09, 0x0c, 0x0c, 0x09, 0x09, 0x0c, 0x09, 0x09, 0x03, 0x00, 0xf7, 0xdc, 0xaf, 0x85, 0x80, 0x82, 0x94, 0xa9, 0xb8, 0xbe, 0xb8, 0xbb, 0xc1, 0xca, 0xd3, 0xd3, 0xd6, 0xd0, 0xcd, 0xd9, 0xe2, 0xdc, 0xc1, 0xa9, 0xb2, 0xd9, 0xf7, 0x0c, 0x1e, 0x2d, 0x3c, 0x3c, 0x3f, 0x39, 0x2d, 0x2d, 0x3c, 0x42, 0x18, 0xac, 0x80, 0x80, 0x80, 0x80, 0x85, 0x8b, 0x80, 0x80, 0x88, 0xbb, 0x03, 0x48, 0x63, 0x51, 0x2a, 0xee, 0xd9, 0x00, 0xfa, 0x00, 0x3c, 0x33, 0x21, 0x45, 0x54, 0x2d, 0x1b, 0x33, 0x42, 0x45, 0x3f, 0x27, 0x21, 0x1b, 0x00, +0x18, 0x54, 0x7b, 0x7f, 0x75, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x75, 0x5d, 0x4b, 0x6f, 0x7f, 0x7f, 0x7f, 0x7f, 0x69, 0x42, 0x2d, 0x00, 0xf4, 0x0c, 0x21, 0x36, 0x2d, 0x3c, 0x21, 0x03, 0x27, 0x2d, 0x21, 0x21, 0x2a, 0x30, 0x3c, 0x27, 0x00, 0x0f, 0x1b, 0x00, 0xf1, 0xdf, 0xa0, 0x82, 0x8b, 0x82, 0x91, 0x91, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x8e, 0xa6, 0xc4, 0xc1, 0xa6, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x8b, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x8b, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x82, 0x9d, 0xc4, 0xb8, 0x91, 0x9a, 0x91, 0x8b, 0xd3, 0x4b, 0x78, 0x45, 0x1b, 0xdf, 0xaf, 0xc4, 0xd6, 0xdf, 0x09, 0x18, 0x0c, 0x1b, 0x2a, 0x24, 0x1e, 0x12, 0xf1, 0xdf, 0xf1, 0xf7, 0x03, 0xf1, 0xd9, 0xee, 0xf4, 0xee, 0xf4, 0x06, 0xe8, 0xcd, 0xee, 0x00, 0x12, 0x2d, 0x42, 0x27, 0xfd, 0x06, 0xf4, 0xfd, 0x09, 0xeb, 0x03, 0x12, 0xf4, 0xe5, 0xd9, 0xe2, 0xfd, 0x1e, 0x24, 0x21, 0x27, 0x27, +0x2d, 0x30, 0x30, 0x24, 0x1e, 0x18, 0x09, 0xfd, 0xf4, 0x12, 0x30, 0x21, 0x39, 0x51, 0x42, 0x48, 0x4b, 0x45, 0x54, 0x57, 0x3f, 0x1b, 0x0f, 0x12, 0x1e, 0x27, 0x33, 0x24, 0x0f, 0x15, 0x27, 0x33, 0x12, 0xf4, 0xe2, 0xcd, 0xd3, 0xdf, 0xe8, 0xf7, 0xf4, 0xdf, 0xd9, 0xe8, 0xf4, 0x03, 0x09, 0x03, 0xf4, 0xf7, 0x03, 0x06, 0x09, 0xfd, 0x06, 0x06, 0xdc, 0xcd, 0xd0, 0xc4, 0xcd, 0xe2, 0xf4, 0xe8, 0xc7, 0xc1, 0xbb, 0xb2, 0xb8, 0xc4, 0xcd, 0xcd, 0xc7, 0xb5, 0x97, 0x8e, 0xa9, 0xa3, 0xa9, 0xc1, 0xaf, 0xa0, 0xa9, 0xca, 0x18, 0x3c, 0x03, 0xdf, 0xc7, 0xa6, 0xaf, 0xd6, 0xee, 0xdf, 0xe8, 0xee, 0xcd, 0xc7, 0xd6, 0xf4, 0x06, 0xe8, 0xbe, 0xb2, 0xbe, 0xd3, 0xd9, 0xca, 0xc1, 0xcd, 0xf7, 0x15, 0x09, 0xe8, 0xdc, 0xeb, 0x03, 0x15, 0x0c, 0xee, 0xd9, 0xdf, 0xee, 0xfa, 0x00, 0x03, 0x09, 0x06, 0x06, 0x15, 0x24, 0x27, 0x1e, 0x15, 0x0f, 0x15, 0x1e, 0x1e, 0x2a, 0x4b, 0x6c, 0x7f, 0x7f, 0x7f, 0x78, 0x60, 0x57, 0x66, 0x72, 0x66, 0x6c, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x78, 0x72, 0x7f, 0x7f, 0x7f, 0x7f, +0x6f, 0x5d, 0x6c, 0x7f, 0x7f, 0x7b, 0x57, 0x33, 0x3f, 0x4e, 0x4b, 0x39, 0x09, 0xe8, 0xd0, 0xd3, 0xf1, 0xfa, 0xfd, 0x1b, 0x18, 0xe8, 0xdf, 0x0f, 0x33, 0x2d, 0x0c, 0xe2, 0xc4, 0xd3, 0x03, 0x1e, 0x27, 0x33, 0x18, 0xe5, 0xd3, 0xd6, 0xe8, 0xfd, 0x00, 0xf4, 0x03, 0x0f, 0x03, 0xee, 0xeb, 0xf7, 0xe8, 0xee, 0xeb, 0xc1, 0xa6, 0x9a, 0xb8, 0xf7, 0x12, 0x06, 0x15, 0xf7, 0xd3, 0xcd, 0xa3, 0xb2, 0xe2, 0xf7, 0xf7, 0xdf, 0xc1, 0xbb, 0xc1, 0xb5, 0x9d, 0x94, 0xa9, 0xbb, 0xc7, 0xb8, 0x97, 0x88, 0x97, 0xb5, 0xbb, 0xa6, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x94, 0xa6, 0x91, 0x80, 0x80, 0x80, 0x80, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x9d, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xa3, 0xbb, 0xd3, 0xca, 0xa0, 0x85, 0x91, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x97, 0xa6, 0x9d, 0x8e, 0x88, 0xa6, 0xca, 0xe2, 0xeb, 0xdc, 0xc7, 0xc4, 0xd0, 0xd3, 0xdf, 0xd9, 0xac, 0xaf, 0xb8, 0xb2, 0xb8, 0xc7, 0xdc, 0xcd, 0xcd, 0xc4, 0xac, 0x97, 0x82, 0x85, 0x8b, 0x9d, 0xb2, 0xa9, 0x94, 0x82, 0x80, 0x88, 0xa0, 0x9d, 0x97, 0xa6, 0x91, 0x85, 0xb2, 0xac, 0x80, 0x80, 0x80, 0x80, 0x80, 0x8b, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x94, 0x9a, 0x8b, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, +0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x82, 0x80, 0x80, 0x80, 0x97, 0x9d, 0x9d, 0x9a, 0x8b, 0x80, 0x80, 0x88, 0x80, 0x80, 0x80, 0x80, 0xaf, 0xd0, 0xd3, 0xbb, 0xaf, 0xbb, 0xb8, 0xa9, 0x80, 0x80, 0x80, 0xb2, 0xe5, 0xfd, 0x0c, 0xfd, 0xdf, 0xca, 0xd3, 0xe5, 0xee, 0x00, 0x09, 0xf4, 0xdc, 0xdf, 0xe8, 0xf7, 0x0c, 0x18, 0x15, 0x06, 0x06, 0x21, 0x33, 0x27, 0x2a, 0x3c, 0x3f, 0x2d, 0x3c, 0x54, 0x66, 0x7f, 0x78, 0x72, 0x69, 0x5d, 0x6f, 0x7b, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, +0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7e, 0x51, 0x51, 0x72, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x6f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x6c, 0x78, 0x7f, 0x7f, 0x7f, 0x63, 0x42, 0x5a, 0x7f, 0x7f, 0x7f, 0x7f, 0x72, 0x51, 0x51, 0x69, 0x75, 0x75, 0x6c, 0x5a, 0x4e, 0x54, 0x66, 0x7b, 0x7f, 0x7f, 0x72, 0x5d, 0x69, 0x7f, 0x7f, 0x7f, 0x51, 0x54, 0x66, 0x7f, 0x7f, 0x7f, 0x72, 0x63, 0x60, 0x5a, 0x45, 0x45, 0x57, 0x36, 0x27, 0x54, 0x72, 0x78, 0x7f, 0x7e, 0x69, 0x48, 0x2a, 0x3c, 0x72, 0x7e, 0x66, 0x42, 0x30, 0x39, 0x54, 0x54, 0x45, 0x48, 0x54, 0x5d, 0x63, 0x69, 0x7b, 0x7f, 0x60, 0x2a, 0x06, 0x00, 0x2a, 0x60, 0x6c, 0x4b, 0x2a, 0x2a, 0x36, 0x42, 0x3c, 0x24, 0x24, 0x36, 0x57, 0x7f, 0x7f, 0x7f, 0x63, 0x3c, 0x18, 0x1b, 0x2d, 0x42, 0x45, 0x4b, 0x57, 0x36, 0x0c, 0x21, 0x69, 0x7f, 0x7f, 0x60, 0x24, 0x18, 0x48, 0x7f, 0x7f, 0x7f, 0x7f, 0x5d, 0x51, 0x5a, 0x5d, 0x5a, 0x69, 0x7e, 0x7f, 0x6c, 0x66, 0x66, 0x60, 0x63, 0x5a, 0x51, 0x57, 0x72, 0x7f, 0x66, 0x57, 0x69, 0x6f, 0x63, 0x42, 0x33, 0x42, 0x5a, 0x6f, 0x63, 0x4b, 0x39, 0x4b, 0x75, 0x7f, 0x7f, 0x7b, 0x72, 0x6c, 0x69, 0x51, 0x45, 0x5a, 0x60, 0x51, 0x4b, 0x4b, 0x2d, 0x09, 0x12, 0x36, 0x69, 0x7f, 0x6c, 0x45, 0x39, 0x36, 0x2d, 0x24, 0x39, 0x4e, 0x51, 0x51, 0x51, 0x4b, 0x4e, 0x54, 0x4e, 0x42, 0x2d, 0x1e, 0x1b, 0x2d, 0x39, 0x2d, 0x0f, 0xf7, 0xee, 0xfd, 0x21, 0x24, 0x0c, 0xeb, 0xd9, 0xdc, 0xf4, 0x09, 0xfd, 0xe2, 0xd3, 0xd0, 0xdc, 0x03, 0x21, 0x12, 0xfd, 0xeb, 0xd9, 0xc7, 0xc7, +0xdc, 0xe5, 0xe5, 0xe5, 0xdf, 0xd3, 0xca, 0xcd, 0xca, 0xc4, 0xd9, 0xe5, 0xdc, 0xbe, 0xa6, 0xac, 0xca, 0xf1, 0x00, 0xe8, 0xd6, 0xc4, 0xb2, 0xc1, 0xd3, 0xd9, 0xd6, 0xc1, 0xbb, 0xca, 0xd6, 0xbe, 0xa3, 0xa9, 0xc1, 0xcd, 0xca, 0xb5, 0xa3, 0xa0, 0xa0, 0xa9, 0xa9, 0xb2, 0xc7, 0xc7, 0xa3, 0x80, 0x80, 0x80, 0x9a, 0xaf, 0xb8, 0xa6, 0x94, 0x85, 0x80, 0x8b, 0x88, 0x85, 0x9d, 0xac, 0x8b, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x8b, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x91, 0xa9, 0xc1, 0xd3, 0xc7, 0xac, 0x9a, 0x9d, 0xa6, 0xa9, 0xbb, 0xc4, 0xc1, 0xb2, 0x94, 0x97, 0xb8, 0xc7, 0xbb, 0xb2, 0xb5, 0xc4, 0xc7, 0xd6, 0xee, 0xf4, 0xe8, 0xcd, 0xbe, 0xc4, 0xc7, 0xd0, 0xd9, 0xeb, 0x06, 0x15, 0x12, 0x00, 0xee, 0xf1, 0x06, 0x27, 0x3f, 0x30, 0x03, 0xf1, 0x03, 0x24, 0x3f, 0x42, 0x27, 0x09, 0xeb, 0xdc, 0xeb, 0x12, 0x27, 0x2d, 0x21, 0x0f, 0x15, 0x2a, 0x3c, 0x3c, 0x18, 0xee, 0xfd, 0x24, 0x30, 0x1e, 0x15, 0x03, 0xdf, 0xcd, 0xd3, 0xf4, 0x1b, 0x36, 0x27, 0x00, 0xdc, 0xca, 0xd6, 0xe5, 0x03, 0x27, 0x2d, 0x09, 0xe8, 0xd9, 0xd6, 0xd3, 0xd6, 0xee, 0x06, 0xf4, 0xbe, +0x94, 0x97, 0xc1, 0xeb, 0xf4, 0xf4, 0xfa, 0x03, 0x00, 0xf4, 0xf1, 0x0c, 0x1e, 0x0f, 0xf1, 0xd3, 0xbe, 0xb5, 0xd0, 0x09, 0x30, 0x33, 0x15, 0xf1, 0xca, 0xaf, 0xbe, 0xdc, 0xeb, 0xe8, 0xd9, 0xd9, 0xe5, 0xf7, 0xfd, 0xee, 0xe8, 0xee, 0xee, 0x03, 0x1b, 0x09, 0xd6, 0xb5, 0xbb, 0xc7, 0xbb, 0xaf, 0xbe, 0xeb, 0x09, 0xf1, 0xbe, 0x9d, 0xa0, 0xa9, 0x9a, 0x91, 0xa9, 0xc7, 0xcd, 0xbb, 0xa3, 0x97, 0x9d, 0xb2, 0xc7, 0xd0, 0xd3, 0xcd, 0xac, 0x9d, 0xa3, 0xa0, 0x9a, 0x8e, 0x85, 0x97, 0xa3, 0x88, 0x80, 0x80, 0x82, 0x97, 0x97, 0x91, 0x9d, 0x9d, 0x8e, 0x94, 0xa0, 0xa0, 0x9a, 0x97, 0x91, 0x97, 0x9d, 0x94, 0x88, 0x88, 0x94, 0xa6, 0x97, 0x80, 0x82, 0xb2, 0xc7, 0xb5, 0xa0, 0xa0, 0xa3, 0x94, 0x80, 0x80, 0x80, 0x94, 0xa9, 0xa9, 0xb2, 0xb8, 0x9d, 0x85, 0xa0, 0xcd, 0xca, 0xb5, 0xa9, 0xac, 0xbe, 0xd0, 0xe5, 0xe5, 0xca, 0xb5, 0xc7, 0xeb, 0xf7, 0xdf, 0xd0, 0xe2, 0xf1, 0xee, 0xf1, 0xf7, 0xe8, 0xe5, 0x00, 0x12, 0x0c, 0xfd, 0xfa, 0xf1, 0xe2, 0xe8, 0xf1, 0x00, 0x15, 0x1b, 0x0f, 0x18, 0x27, 0x12, 0x00, 0x09, 0x1e, 0x33, 0x27, 0x0c, 0x06, 0x12, 0x24, 0x39, 0x4b, 0x51, 0x4b, 0x3c, 0x30, 0x2a, 0x2a, 0x33, 0x2d, 0x24, 0x12, 0x09, 0x12, 0x21, 0x36, 0x48, 0x45, 0x2d, 0x1b, 0x1b, 0x27, 0x30, 0x2d, 0x2d, 0x33, 0x3f, 0x4e, 0x54, 0x57, 0x5a, 0x54, 0x51, 0x51, 0x36, 0x12, 0x12, 0x24, 0x45, 0x60, 0x66, 0x60, 0x51, 0x3c, 0x30, 0x39, 0x51, 0x63, 0x5d, 0x3f, 0x30, 0x36, 0x54, 0x7b, 0x7b, 0x5d, 0x51, 0x5a, 0x57, 0x45, 0x42, 0x60, 0x7f, 0x7f, 0x7f, 0x63, 0x69, 0x66, 0x57, 0x57, 0x5d, 0x54, 0x4b, 0x45, 0x42, 0x54, 0x69, 0x6f, 0x63, 0x51, 0x4b, 0x48, 0x45, 0x4e, 0x63, +0x69, 0x5d, 0x3f, 0x36, 0x45, 0x45, 0x3c, 0x30, 0x3c, 0x54, 0x51, 0x36, 0x30, 0x45, 0x4e, 0x3f, 0x27, 0x24, 0x30, 0x45, 0x48, 0x3f, 0x30, 0x1e, 0x0c, 0x06, 0x1b, 0x33, 0x36, 0x27, 0x12, 0x00, 0x09, 0x24, 0x27, 0x1e, 0x1e, 0x1e, 0x09, 0xe5, 0xdc, 0xf4, 0x09, 0x06, 0x00, 0x03, 0xfd, 0xee, 0xf1, 0x06, 0x2a, 0x48, 0x45, 0x21, 0x00, 0xf7, 0xfa, 0x00, 0x03, 0xfd, 0xfd, 0xfa, 0xf1, 0xdf, 0xdc, 0xf4, 0x09, 0x09, 0xf7, 0xdf, 0xd0, 0xe5, 0x03, 0x0c, 0x00, 0xeb, 0xdc, 0xcd, 0xbe, 0xc7, 0xe2, 0x00, 0x0c, 0x0f, 0x0c, 0xf7, 0xe5, 0xdf, 0xe8, 0xee, 0xdf, 0xcd, 0xd0, 0xee, 0x06, 0x09, 0xe5, 0xbb, 0xac, 0xac, 0xbe, 0xdc, 0xe8, 0xe2, 0xd9, 0xdc, 0xee, 0xfd, 0xe8, 0xc1, 0xbe, 0xca, 0xcd, 0xc1, 0xb8, 0xcd, 0xe8, 0x00, 0x09, 0x03, 0xf7, 0xe8, 0xdc, 0xdc, 0xe2, 0xe2, 0xe5, 0xe8, 0xee, 0xf4, 0xf1, 0xe8, 0xe8, 0x00, 0x15, 0x1b, 0x0f, 0x03, 0x0f, 0x1b, 0x21, 0x0f, 0xee, 0xf4, 0x1b, 0x2a, 0x2a, 0x2d, 0x27, 0x21, 0x33, 0x42, 0x39, 0x27, 0x1b, 0x24, 0x33, 0x30, 0x24, 0x24, 0x27, 0x2a, 0x30, 0x33, 0x3c, 0x51, 0x60, 0x57, 0x45, 0x33, 0x30, 0x39, 0x42, 0x51, 0x54, 0x48, 0x2d, 0x27, 0x45, 0x69, 0x75, 0x66, 0x5d, 0x5a, 0x54, 0x4e, 0x4e, 0x57, 0x66, 0x7b, 0x7f, 0x7e, 0x72, 0x63, 0x63, 0x6c, 0x7b, 0x7f, 0x7f, 0x7f, 0x7f, 0x72, 0x60, 0x69, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x57, 0x39, 0x45, 0x7b, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x75, 0x6f, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7b, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x78, 0x75, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x78, 0x75, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7b, 0x6c, 0x51, +0x3f, 0x48, 0x5a, 0x66, 0x6c, 0x78, 0x7b, 0x69, 0x57, 0x60, 0x69, 0x63, 0x5a, 0x51, 0x4b, 0x39, 0x2d, 0x3c, 0x5d, 0x6f, 0x60, 0x45, 0x3f, 0x57, 0x6c, 0x66, 0x54, 0x4e, 0x60, 0x6f, 0x75, 0x6f, 0x5a, 0x3c, 0x15, 0xfa, 0x15, 0x4b, 0x54, 0x33, 0x1e, 0x27, 0x39, 0x36, 0x1e, 0x0c, 0x15, 0x30, 0x45, 0x39, 0x1b, 0x06, 0x0c, 0x27, 0x42, 0x51, 0x3f, 0x18, 0xfd, 0x03, 0x27, 0x3f, 0x48, 0x48, 0x39, 0x0f, 0xe5, 0xd9, 0xdf, 0xe8, 0xf1, 0x03, 0x15, 0x15, 0x06, 0xf4, 0xf1, 0xf4, 0xfa, 0x00, 0xfd, 0xfa, 0xf7, 0xee, 0xf1, 0xf7, 0xf4, 0xf1, 0xeb, 0xdf, 0xd9, 0xdc, 0xf1, 0x06, 0x03, 0xee, 0xca, 0xbb, 0xc7, 0xd9, 0xeb, 0xfd, 0x03, 0xee, 0xc1, 0xb2, 0xd3, 0xf1, 0xeb, 0xd9, 0xd0, 0xd3, 0xca, 0xb5, 0xaf, 0xc7, 0xe2, 0xeb, 0xe2, 0xd3, 0xc7, 0xb2, 0x9d, 0xa3, 0xcd, 0xfa, 0xfa, 0xe2, 0xd0, 0xca, 0xc1, 0xb2, 0xb5, 0xb8, 0xaf, 0xa9, 0xb2, 0xaf, 0xa6, 0xa3, 0xa3, 0xa9, 0xb5, 0xc1, 0xbe, 0xac, 0xac, 0xbb, 0xcd, 0xe8, 0xee, 0xc4, 0x97, 0x9d, 0xc1, 0xd6, 0xcd, 0xbe, 0xc1, 0xd0, 0xd0, 0xc4, 0xc4, 0xd0, 0xd3, 0xc7, 0xcd, 0xe8, 0xf4, 0xe8, 0xe2, 0xe8, 0xe2, 0xd0, 0xc7, 0xd6, 0xe8, 0xe5, 0xdf, 0xe5, 0xfa, 0x0c, 0x15, 0x1e, 0x12, 0xee, 0xc4, 0xb2, 0xc7, 0xf1, 0x12, 0x0c, 0xee, 0xe5, 0xe8, 0xe2, 0xdf, 0xf1, 0xfd, 0xf7, 0xee, 0xee, 0xf4, 0xf7, 0xfd, 0x0f, 0x1e, 0x1e, 0x0f, 0x0c, 0x0c, 0x0f, 0x0f, 0x06, 0x03, 0x09, 0x09, 0x03, 0x06, 0x03, 0xfd, 0x09, 0x12, 0x18, 0x2a, 0x30, 0x21, 0x0f, 0x12, 0x24, 0x30, 0x2a, 0x1b, 0x18, 0x18, 0x15, 0x21, 0x21, 0x0c, 0xfd, 0xfa, 0x0c, 0x15, 0x03, 0xf7, 0x0c, 0x30, 0x33, 0x21, 0x1b, 0x0f, 0xfa, 0xf4, 0x00, 0x12, 0x21, +0x1e, 0x0f, 0xfd, 0xfa, 0xfd, 0x03, 0x0c, 0x12, 0x0c, 0x00, 0x0c, 0x1e, 0x18, 0x06, 0xf7, 0xeb, 0xe8, 0xe8, 0xe2, 0xdf, 0xe2, 0xe8, 0xf1, 0xfd, 0xf4, 0xdc, 0xd6, 0xeb, 0xf7, 0xf4, 0xee, 0xf1, 0xee, 0xdf, 0xd3, 0xcd, 0xc4, 0xc4, 0xd0, 0xe2, 0xeb, 0xeb, 0xe8, 0xd9, 0xc7, 0xc7, 0xdf, 0xe8, 0xdf, 0xca, 0xb5, 0xa0, 0x97, 0xa9, 0xbb, 0xb8, 0xa9, 0xa0, 0xa0, 0xb2, 0xc7, 0xd3, 0xc7, 0xb5, 0xb5, 0xbb, 0xb8, 0xaf, 0xa9, 0xa9, 0xa9, 0xac, 0xa9, 0xb2, 0xb5, 0xa0, 0x80, 0x8b, 0xb5, 0xd9, 0xdc, 0xbe, 0xa3, 0x8e, 0x85, 0x8e, 0x9d, 0xaf, 0xbe, 0xb8, 0x9a, 0x85, 0x80, 0x80, 0x85, 0x94, 0xa9, 0xb8, 0xbe, 0xa9, 0x94, 0x91, 0x88, 0x80, 0x80, 0x80, 0x8b, 0x94, 0x8e, 0x88, 0x85, 0x80, 0x80, 0x80, 0x80, 0x85, 0xa0, 0xb5, 0xb5, 0x91, 0x80, 0x80, 0x80, 0x80, 0x8b, 0x97, 0x9d, 0x97, 0x8e, 0x88, 0x88, 0x85, 0x8b, 0x94, 0x94, 0x91, 0x91, 0x91, 0x97, 0x9a, 0x91, 0x88, 0x91, 0xa6, 0xac, 0xa0, 0x8b, 0x82, 0x8b, 0x8e, 0x88, 0x8b, 0x94, 0xa3, 0xb5, 0xc1, 0xc7, 0xc1, 0xb5, 0xa3, 0x9a, 0xa3, 0xbb, 0xcd, 0xc4, 0xaf, 0xa0, 0xa0, 0xa6, 0xaf, 0xb5, 0xb2, 0xac, 0xa9, 0xb8, 0xc1, 0xc4, 0xca, 0xd0, 0xd0, 0xcd, 0xca, 0xca, 0xd3, 0xdf, 0xd9, 0xcd, 0xd0, 0xe2, 0xf7, 0xf7, 0xe8, 0xd9, 0xd9, 0xdf, 0xeb, 0xf1, 0xf1, 0xf4, 0xf7, 0xf7, 0xf4, 0xee, 0xf4, 0x00, 0x0f, 0x12, 0x06, 0xfa, 0xfa, 0xfd, 0x03, 0x09, 0x0c, 0x12, 0x1b, 0x24, 0x1e, 0x12, 0x0c, 0x18, 0x27, 0x30, 0x42, 0x54, 0x51, 0x30, 0x12, 0x0c, 0x18, 0x1e, 0x2a, 0x42, 0x4b, 0x42, 0x2a, 0x15, 0x06, 0x12, 0x33, 0x51, 0x57, 0x4b, 0x3f, 0x39, 0x3c, 0x3f, 0x36, 0x30, 0x30, 0x3c, 0x51, 0x5a, 0x51, 0x48, 0x42, +0x3c, 0x30, 0x2a, 0x30, 0x30, 0x30, 0x33, 0x39, 0x42, 0x5d, 0x7b, 0x7e, 0x66, 0x4b, 0x42, 0x48, 0x51, 0x4e, 0x36, 0x21, 0x1e, 0x1e, 0x21, 0x27, 0x30, 0x39, 0x39, 0x3c, 0x48, 0x51, 0x51, 0x45, 0x39, 0x36, 0x42, 0x5d, 0x60, 0x39, 0x0c, 0x03, 0x24, 0x45, 0x45, 0x36, 0x30, 0x30, 0x33, 0x3c, 0x3c, 0x2d, 0x21, 0x2a, 0x36, 0x30, 0x21, 0x27, 0x3c, 0x3f, 0x27, 0x15, 0x2a, 0x45, 0x33, 0x0f, 0xfa, 0xf4, 0xf4, 0xfd, 0x12, 0x27, 0x39, 0x39, 0x2a, 0x1e, 0x1e, 0x24, 0x1e, 0x15, 0x12, 0x18, 0x18, 0x0c, 0x06, 0x0c, 0x12, 0x18, 0x15, 0x1b, 0x15, 0xfd, 0xee, 0xfa, 0x15, 0x27, 0x1b, 0x09, 0x00, 0x06, 0x0f, 0x1b, 0x1e, 0x12, 0x03, 0x06, 0x18, 0x21, 0x18, 0x06, 0xf4, 0x00, 0x15, 0x1e, 0x21, 0x1e, 0x12, 0x06, 0x03, 0x06, 0x09, 0x00, 0xfa, 0x06, 0x0c, 0x00, 0xf4, 0xf7, 0x0c, 0x27, 0x3c, 0x3c, 0x30, 0x21, 0x1e, 0x1b, 0x06, 0xe2, 0xd6, 0xf7, 0x21, 0x2d, 0x1e, 0x09, 0xfd, 0x03, 0x0c, 0x0f, 0x0c, 0x12, 0x0f, 0xf7, 0xeb, 0x03, 0x27, 0x33, 0x27, 0x09, 0xf4, 0xf4, 0x00, 0x06, 0xf7, 0xeb, 0xf7, 0x0f, 0x18, 0x06, 0xf4, 0xf7, 0x09, 0x21, 0x24, 0x0f, 0xf7, 0xf4, 0xf4, 0xee, 0xee, 0xfa, 0x0c, 0x0c, 0x00, 0xf7, 0xf1, 0xfa, 0x0f, 0x1e, 0x0f, 0xfd, 0xfa, 0x09, 0x21, 0x36, 0x36, 0x24, 0x0f, 0x09, 0x09, 0xfd, 0xeb, 0xe8, 0xf4, 0x00, 0x0f, 0x21, 0x1e, 0x0c, 0x03, 0x06, 0x03, 0xfa, 0xf4, 0x03, 0x1b, 0x2a, 0x30, 0x33, 0x2d, 0x21, 0x15, 0x0f, 0x18, 0x1e, 0x0f, 0x00, 0x00, 0x06, 0xf7, 0xe2, 0xee, 0x0f, 0x2d, 0x33, 0x2a, 0x21, 0x15, 0x00, 0xe2, 0xdc, 0xfa, 0x1e, 0x27, 0x18, 0x0f, 0x0f, 0x09, 0x06, 0x03, 0x00, 0x0c, 0x1e, 0x21, 0x0c, 0xfd, 0x03, 0x12, 0x21, +0x24, 0x18, 0x09, 0x0c, 0x12, 0x0f, 0x06, 0x06, 0x09, 0x0f, 0x0c, 0x03, 0x00, 0xfd, 0x00, 0x06, 0x15, 0x24, 0x27, 0x21, 0x12, 0xfd, 0xfd, 0x1b, 0x33, 0x24, 0xfa, 0xe5, 0xee, 0x00, 0x09, 0x06, 0x03, 0x09, 0x15, 0x24, 0x24, 0x12, 0x09, 0x0f, 0x18, 0x12, 0xfd, 0xeb, 0xd6, 0xc7, 0xdf, 0x12, 0x39, 0x36, 0x1e, 0x0f, 0x12, 0x12, 0x03, 0xf7, 0xfd, 0x09, 0x06, 0xfa, 0xfa, 0x03, 0x09, 0x06, 0xfa, 0xf1, 0xf4, 0xfd, 0x03, 0x0c, 0x0c, 0x06, 0x06, 0x15, 0x15, 0x09, 0xf7, 0xe8, 0xdf, 0xf4, 0x0f, 0x15, 0x00, 0xf1, 0xf4, 0xfa, 0xfa, 0x00, 0x0c, 0x0c, 0x06, 0xf7, 0xf1, 0x00, 0x03, 0xee, 0xd6, 0xdc, 0xfa, 0x0c, 0xfd, 0xe5, 0xe2, 0xfa, 0x0c, 0x09, 0xfd, 0xeb, 0xd3, 0xc4, 0xc7, 0xdc, 0xf4, 0x03, 0x00, 0xf7, 0xe5, 0xd6, 0xd9, 0xe2, 0xeb, 0xeb, 0xe5, 0xdf, 0xdc, 0xdc, 0xd9, 0xd6, 0xd9, 0xdc, 0xd9, 0xd0, 0xca, 0xcd, 0xdf, 0xe2, 0xd3, 0xcd, 0xcd, 0xd9, 0xe8, 0xe8, 0xd9, 0xc7, 0xcd, 0xd6, 0xd0, 0xc4, 0xb5, 0xaf, 0xb5, 0xca, 0xd9, 0xcd, 0xc1, 0xbe, 0xd0, 0xe5, 0xe8, 0xdf, 0xd3, 0xcd, 0xcd, 0xe2, 0xf4, 0xee, 0xdc, 0xc4, 0xb5, 0xaf, 0xb8, 0xc4, 0xc4, 0xc1, 0xbb, 0xc7, 0xd9, 0xe2, 0xd9, 0xc1, 0xb2, 0xb8, 0xc4, 0xd6, 0xe5, 0xe5, 0xd6, 0xca, 0xc4, 0xc7, 0xd3, 0xdc, 0xdf, 0xd6, 0xd6, 0xdf, 0xd9, 0xd0, 0xca, 0xca, 0xcd, 0xd9, 0xf1, 0xfd, 0xeb, 0xd6, 0xd3, 0xd9, 0xdf, 0xe2, 0xeb, 0xe2, 0xca, 0xc4, 0xd3, 0xee, 0xfa, 0xf4, 0xee, 0xeb, 0xeb, 0xf1, 0xfa, 0x09, 0x0c, 0x00, 0xf4, 0xeb, 0xe8, 0xee, 0xf7, 0x00, 0x0c, 0x0f, 0x09, 0xfd, 0xf4, 0xf7, 0xfa, 0xfa, 0xf7, 0xfd, 0x03, 0xfd, 0xf4, 0xf4, 0xf7, 0xfd, 0x06, 0x0f, 0x18, 0x1e, 0x1e, 0x21, 0x21, 0x18, +0x15, 0x18, 0x12, 0xfd, 0xf1, 0xf1, 0xfd, 0x09, 0x0c, 0x0c, 0x15, 0x21, 0x1b, 0x0c, 0x03, 0x06, 0x03, 0xfd, 0x03, 0x15, 0x24, 0x2d, 0x30, 0x27, 0x1b, 0x12, 0x0f, 0x12, 0x0f, 0x0c, 0x09, 0x0c, 0x1e, 0x39, 0x45, 0x36, 0x1b, 0x0f, 0x15, 0x18, 0x12, 0x0f, 0x0f, 0x0f, 0x06, 0x06, 0x09, 0x12, 0x1e, 0x2a, 0x21, 0x0f, 0x00, 0xfd, 0x00, 0x09, 0x12, 0x1b, 0x21, 0x1b, 0x12, 0x12, 0x1b, 0x2a, 0x27, 0x18, 0x0f, 0x0c, 0x0f, 0x12, 0x0c, 0x00, 0xf7, 0xf7, 0x00, 0x0c, 0x15, 0x12, 0x0f, 0x0f, 0x0c, 0x06, 0x09, 0x15, 0x21, 0x1e, 0x12, 0x06, 0x09, 0x15, 0x1b, 0x15, 0x0c, 0x09, 0xfd, 0xe8, 0xdf, 0xee, 0x00, 0x09, 0x06, 0x06, 0x0c, 0x0f, 0x09, 0x00, 0xf7, 0xf1, 0xf1, 0xfd, 0x0f, 0x1e, 0x18, 0xfa, 0xe8, 0xf1, 0x12, 0x27, 0x21, 0x0c, 0xf4, 0xe2, 0xdf, 0xeb, 0xfd, 0x12, 0x18, 0x0f, 0xf7, 0xe2, 0xe2, 0xfa, 0x1e, 0x33, 0x33, 0x21, 0x06, 0xee, 0xe8, 0x00, 0x21, 0x2a, 0x12, 0xfa, 0xfa, 0x0f, 0x21, 0x1b, 0x06, 0xf4, 0xf7, 0xfa, 0xfa, 0xfa, 0xfd, 0x00, 0xfd, 0xf7, 0xf7, 0xf7, 0xfd, 0xfd, 0x00, 0x12, 0x2a, 0x30, 0x1b, 0x03, 0x06, 0x12, 0x12, 0x0f, 0x12, 0x0c, 0xf4, 0xe2, 0xe8, 0xfd, 0x09, 0x09, 0x03, 0x00, 0x09, 0x15, 0x1b, 0x21, 0x21, 0x15, 0xfa, 0xe5, 0xe8, 0xf7, 0x06, 0x18, 0x21, 0x21, 0x1e, 0x1b, 0x12, 0x0c, 0x0c, 0x0c, 0x09, 0x00, 0xfd, 0x0c, 0x1e, 0x1b, 0x0c, 0xfd, 0xfa, 0x03, 0x09, 0x0f, 0x0f, 0x06, 0x00, 0x03, 0x06, 0x0c, 0x1e, 0x2d, 0x2a, 0x1b, 0x0c, 0x06, 0x09, 0x12, 0x15, 0x0c, 0x03, 0x12, 0x24, 0x24, 0x1b, 0x18, 0x15, 0x0f, 0x06, 0x00, 0x09, 0x15, 0x0f, 0xfd, 0xf1, 0xfa, 0x12, 0x24, 0x2a, 0x24, 0x18, 0x03, 0xf4, 0xf1, 0xf7, 0x09, +0x15, 0x15, 0x0f, 0x06, 0x00, 0x06, 0x12, 0x15, 0x0c, 0x09, 0x0c, 0x0f, 0x09, 0x00, 0xfa, 0xfd, 0xfa, 0xfa, 0x00, 0x00, 0xf7, 0xf1, 0xf7, 0x00, 0x00, 0xf7, 0xf7, 0xfa, 0xf4, 0xe8, 0xe2, 0xe5, 0xf7, 0x0f, 0x1b, 0x15, 0x09, 0x06, 0x09, 0x0c, 0x03, 0xe8, 0xd3, 0xcd, 0xcd, 0xd6, 0xe5, 0xf7, 0x00, 0x00, 0xfd, 0x00, 0xfd, 0xf1, 0xe8, 0xe8, 0xe5, 0xd9, 0xd3, 0xe2, 0xfa, 0x09, 0x06, 0xf7, 0xeb, 0xe5, 0xdf, 0xe5, 0xf4, 0xf7, 0xf4, 0xee, 0xee, 0xf1, 0xfa, 0xfa, 0xf7, 0xf4, 0xeb, 0xd9, 0xd0, 0xd3, 0xe2, 0xee, 0xe5, 0xd6, 0xd9, 0xeb, 0xfd, 0x03, 0x06, 0x03, 0xf4, 0xe2, 0xe2, 0xeb, 0xf7, 0xfd, 0x00, 0x00, 0xf4, 0xeb, 0xee, 0xf7, 0xf7, 0xeb, 0xe8, 0xf4, 0x00, 0x03, 0x06, 0x00, 0xf1, 0xe2, 0xe8, 0xfd, 0x03, 0xfa, 0xe8, 0xd9, 0xdf, 0xf1, 0x03, 0x0f, 0x12, 0x00, 0xee, 0xf1, 0x00, 0x03, 0xf7, 0xee, 0xf4, 0x09, 0x1b, 0x1b, 0x03, 0xee, 0xf1, 0x09, 0x12, 0x03, 0xeb, 0xdf, 0xdf, 0xee, 0x03, 0x0f, 0x06, 0xf7, 0xf1, 0xf4, 0xf7, 0xfa, 0xfa, 0xfa, 0xf7, 0xf7, 0xfa, 0x00, 0x0f, 0x15, 0x0f, 0xfd, 0xf4, 0xf4, 0xfa, 0x00, 0x09, 0x0f, 0x0f, 0xfd, 0xee, 0xe8, 0xee, 0xfd, 0x09, 0x0c, 0xfd, 0xee, 0xe8, 0xee, 0xfa, 0x03, 0x00, 0xf1, 0xe8, 0xee, 0xf7, 0xfd, 0x00, 0xfd, 0xf4, 0xeb, 0xe5, 0xe5, 0xf1, 0xf1, 0xe2, 0xd3, 0xd0, 0xd3, 0xd6, 0xdf, 0xe5, 0xe2, 0xd9, 0xd6, 0xdf, 0xe5, 0xdf, 0xd3, 0xdc, 0xee, 0xf7, 0xee, 0xdf, 0xd3, 0xca, 0xc7, 0xd0, 0xe2, 0xe5, 0xd6, 0xc4, 0xc1, 0xd0, 0xe2, 0xe5, 0xdc, 0xd6, 0xd3, 0xd3, 0xdc, 0xeb, 0xf1, 0xe8, 0xe2, 0xd9, 0xd3, 0xd0, 0xd6, 0xe2, 0xe8, 0xe5, 0xe5, 0xe2, 0xd6, 0xd0, 0xd6, 0xe2, 0xeb, 0xeb, 0xe5, 0xd6, 0xc1, +0xbe, 0xcd, 0xd6, 0xd6, 0xdc, 0xe8, 0xe8, 0xdc, 0xd0, 0xd3, 0xd9, 0xdc, 0xdc, 0xdc, 0xdf, 0xee, 0xfa, 0x09, 0x0c, 0x06, 0xf7, 0xe2, 0xdf, 0xf1, 0xfd, 0xf4, 0xe2, 0xdc, 0xe5, 0xeb, 0xeb, 0xf4, 0x00, 0x00, 0xfa, 0xf4, 0xfa, 0x09, 0x06, 0xf7, 0xf4, 0xfa, 0xf7, 0xf7, 0xfa, 0x00, 0x06, 0x0c, 0x06, 0xfd, 0xfd, 0xfd, 0xfa, 0xeb, 0xe5, 0xf1, 0xfd, 0xf4, 0xf4, 0xfd, 0x06, 0x09, 0x00, 0xf7, 0xfa, 0x00, 0x03, 0x06, 0x09, 0x09, 0x0f, 0x0f, 0x0c, 0x09, 0x03, 0x03, 0x06, 0x06, 0x06, 0x0c, 0x15, 0x15, 0x12, 0x1b, 0x21, 0x18, 0xfd, 0xe2, 0xe2, 0xfd, 0x1b, 0x24, 0x21, 0x1e, 0x18, 0x0f, 0x0c, 0x09, 0x00, 0xfd, 0x00, 0x09, 0x12, 0x12, 0x03, 0xfd, 0x0c, 0x1e, 0x2a, 0x21, 0x0c, 0xfd, 0x00, 0x0f, 0x18, 0x15, 0x12, 0x1b, 0x24, 0x27, 0x2a, 0x27, 0x18, 0x06, 0x03, 0x0f, 0x21, 0x27, 0x1e, 0x18, 0x18, 0x18, 0x1e, 0x2a, 0x30, 0x2a, 0x27, 0x2a, 0x2a, 0x24, 0x1b, 0x12, 0x12, 0x1b, 0x30, 0x3c, 0x33, 0x1e, 0x12, 0x0f, 0x21, 0x36, 0x3f, 0x3c, 0x36, 0x36, 0x36, 0x3c, 0x39, 0x2d, 0x1b, 0x15, 0x1e, 0x27, 0x2a, 0x30, 0x30, 0x2a, 0x1e, 0x1e, 0x24, 0x21, 0x18, 0x18, 0x1e, 0x1b, 0x1e, 0x24, 0x27, 0x1e, 0x18, 0x18, 0x2a, 0x3c, 0x36, 0x15, 0xfa, 0x00, 0x15, 0x27, 0x27, 0x21, 0x1b, 0x15, 0x0c, 0x09, 0x18, 0x2d, 0x30, 0x24, 0x12, 0x0c, 0x0f, 0x18, 0x21, 0x24, 0x1b, 0x0f, 0x03, 0xf7, 0x03, 0x1b, 0x2a, 0x2a, 0x1e, 0x12, 0x09, 0x03, 0x06, 0x0c, 0x15, 0x15, 0x09, 0xfd, 0xfd, 0x09, 0x0f, 0x0c, 0x09, 0x09, 0x06, 0x09, 0x15, 0x21, 0x21, 0x0c, 0xf4, 0xe8, 0xe5, 0xe8, 0xeb, 0xf1, 0xfd, 0x06, 0x03, 0xf7, 0xf1, 0x03, 0x1b, 0x24, 0x18, 0x03, 0xfd, 0x03, 0x00, 0xf7, +0xee, 0xee, 0xfa, 0x09, 0x06, 0xfa, 0xf4, 0xf4, 0xf4, 0x00, 0x18, 0x1e, 0x0f, 0x03, 0xf7, 0xf1, 0xf4, 0xfd, 0x0f, 0x18, 0x12, 0x06, 0x03, 0x09, 0x0c, 0x0c, 0x12, 0x15, 0x0f, 0x06, 0x00, 0x03, 0x0f, 0x1e, 0x24, 0x18, 0x09, 0x03, 0x09, 0x09, 0x06, 0x09, 0x12, 0x1b, 0x21, 0x21, 0x24, 0x24, 0x18, 0x0c, 0x09, 0x18, 0x27, 0x27, 0x1e, 0x18, 0x12, 0x0c, 0x09, 0x0f, 0x18, 0x1b, 0x15, 0x0c, 0x0f, 0x18, 0x18, 0x0c, 0xfd, 0x00, 0x12, 0x24, 0x2d, 0x21, 0x06, 0xeb, 0xee, 0x09, 0x24, 0x27, 0x21, 0x15, 0x06, 0x03, 0x09, 0x0c, 0x0c, 0x0c, 0x09, 0x03, 0x06, 0x0f, 0x18, 0x0f, 0x00, 0xfd, 0xfd, 0xf4, 0xe8, 0xe8, 0xee, 0xee, 0xf1, 0xfa, 0x06, 0x06, 0xf4, 0xe2, 0xe5, 0xfa, 0x06, 0xfa, 0xe8, 0xe2, 0xe5, 0xf1, 0xf7, 0xf4, 0xe8, 0xdc, 0xe2, 0xfa, 0x0c, 0x09, 0xfd, 0xf7, 0xf1, 0xee, 0xee, 0xf4, 0xf7, 0xf7, 0xee, 0xe8, 0xe5, 0xe8, 0xeb, 0xeb, 0xe5, 0xdf, 0xd9, 0xd6, 0xd6, 0xdc, 0xeb, 0xf7, 0xfa, 0xf1, 0xeb, 0xe2, 0xdf, 0xe2, 0xe8, 0xeb, 0xf4, 0xfa, 0xfa, 0xfa, 0xfd, 0xf4, 0xe2, 0xd3, 0xd9, 0xee, 0xfa, 0xf7, 0xee, 0xee, 0xeb, 0xe5, 0xe2, 0xe5, 0xe5, 0xdf, 0xdc, 0xd6, 0xd9, 0xe5, 0xee, 0xeb, 0xdc, 0xd6, 0xd9, 0xdc, 0xe5, 0xf1, 0xfd, 0xfa, 0xf1, 0xeb, 0xe5, 0xe2, 0xe2, 0xe5, 0xeb, 0xe8, 0xdf, 0xd6, 0xd3, 0xd9, 0xe5, 0xee, 0xee, 0xe2, 0xd3, 0xd9, 0xeb, 0xfa, 0xf7, 0xf1, 0xf1, 0xf4, 0xf1, 0xeb, 0xe2, 0xe2, 0xe2, 0xe2, 0xe5, 0xe8, 0xe8, 0xe5, 0xe8, 0xf1, 0xf7, 0xf1, 0xe8, 0xeb, 0xf4, 0xfa, 0xf4, 0xee, 0xeb, 0xe5, 0xe2, 0xe8, 0xf4, 0x00, 0x06, 0x00, 0xf4, 0xeb, 0xe8, 0xee, 0xfa, 0xfd, 0xfa, 0xf1, 0xe8, 0xe5, 0xe8, 0xee, 0xeb, 0xe5, 0xe5, 0xee, +0xee, 0xe2, 0xdf, 0xeb, 0xf4, 0xfa, 0x00, 0x03, 0x00, 0xfd, 0xfa, 0xfa, 0xf7, 0xf7, 0xf7, 0xee, 0xeb, 0xee, 0xfa, 0x03, 0x03, 0xf7, 0xee, 0xeb, 0xeb, 0xe5, 0xe5, 0xeb, 0xf4, 0xf4, 0xf4, 0xf7, 0xf4, 0xee, 0xf1, 0xf7, 0xf1, 0xe8, 0xee, 0xeb, 0xdf, 0xd6, 0xd6, 0xd9, 0xe2, 0xee, 0xf7, 0xf7, 0xf1, 0xeb, 0xe8, 0xe5, 0xe8, 0xeb, 0xe5, 0xdf, 0xd9, 0xdc, 0xdf, 0xe2, 0xe5, 0xe5, 0xe5, 0xe5, 0xe2, 0xe2, 0xe5, 0xeb, 0xe8, 0xe5, 0xe5, 0xe2, 0xdf, 0xe5, 0xee, 0xee, 0xeb, 0xe8, 0xe2, 0xd6, 0xcd, 0xca, 0xd6, 0xe5, 0xf4, 0xfa, 0xf1, 0xe5, 0xdf, 0xe2, 0xee, 0xf4, 0xe8, 0xd6, 0xcd, 0xd6, 0xe5, 0xeb, 0xe8, 0xe8, 0xe8, 0xeb, 0xeb, 0xeb, 0xee, 0xf1, 0xeb, 0xdf, 0xe5, 0xf7, 0x00, 0xfa, 0xe8, 0xdf, 0xe2, 0xe5, 0xeb, 0xf1, 0xf1, 0xee, 0xee, 0xf4, 0xfa, 0xfa, 0xf4, 0xee, 0xf1, 0xfa, 0xfa, 0xf4, 0xf1, 0xeb, 0xeb, 0xf7, 0x09, 0x12, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x09, 0xfd, 0xf4, 0xfa, 0x09, 0x15, 0x1b, 0x1e, 0x1b, 0x18, 0x12, 0x12, 0x1b, 0x1e, 0x15, 0x09, 0x06, 0x09, 0x0c, 0x0c, 0x12, 0x18, 0x1b, 0x24, 0x2a, 0x2a, 0x1e, 0x1b, 0x1e, 0x21, 0x21, 0x1e, 0x1b, 0x15, 0x12, 0x18, 0x2a, 0x36, 0x33, 0x21, 0x12, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x12, 0x1b, 0x1e, 0x1e, 0x21, 0x27, 0x2d, 0x30, 0x2d, 0x21, 0x15, 0x12, 0x15, 0x1b, 0x24, 0x2a, 0x21, 0x18, 0x15, 0x1e, 0x2d, 0x36, 0x36, 0x2a, 0x12, 0x0c, 0x12, 0x15, 0x0f, 0x06, 0x06, 0x15, 0x27, 0x36, 0x33, 0x24, 0x18, 0x15, 0x12, 0x12, 0x12, 0x0f, 0x0c, 0x09, 0x0c, 0x12, 0x18, 0x1b, 0x1b, 0x15, 0x09, 0x06, 0x0f, 0x18, 0x1b, 0x1b, 0x12, 0x06, 0x00, 0x00, 0x06, 0x0f, 0x15, 0x18, 0x18, 0x0f, 0x09, 0x0f, 0x1e, +0x24, 0x21, 0x18, 0x0c, 0x0c, 0x18, 0x1b, 0x12, 0x0c, 0x0c, 0x0c, 0x0f, 0x18, 0x24, 0x27, 0x21, 0x15, 0x0f, 0x0c, 0x06, 0x09, 0x09, 0x06, 0x09, 0x0f, 0x0c, 0x03, 0x06, 0x0f, 0x18, 0x18, 0x12, 0x09, 0x03, 0x09, 0x12, 0x18, 0x18, 0x0f, 0x03, 0xfa, 0xfa, 0x03, 0x0f, 0x1b, 0x1e, 0x1b, 0x12, 0x0c, 0x09, 0x03, 0x00, 0x00, 0x00, 0x03, 0x03, 0xf7, 0xee, 0xee, 0xf7, 0x06, 0x0c, 0x0c, 0x09, 0x00, 0xee, 0xdf, 0xdf, 0xee, 0x03, 0x0c, 0x09, 0x00, 0xfd, 0xf7, 0xf1, 0xf1, 0xf7, 0xfa, 0xf4, 0xeb, 0xe5, 0xe5, 0xeb, 0xf1, 0xf7, 0xfa, 0x00, 0x00, 0xfa, 0xf4, 0xf1, 0xee, 0xe5, 0xe5, 0xeb, 0xf4, 0x00, 0x06, 0xfa, 0xe5, 0xdf, 0xe5, 0xeb, 0xf1, 0xf7, 0xfa, 0xf4, 0xe8, 0xe5, 0xe8, 0xee, 0xf4, 0xf7, 0xf1, 0xeb, 0xf4, 0xfd, 0xfd, 0xf4, 0xe8, 0xdf, 0xe2, 0xf1, 0x00, 0x00, 0xf7, 0xeb, 0xe8, 0xeb, 0xee, 0xe5, 0xdc, 0xdc, 0xe5, 0xf1, 0xf7, 0xf4, 0xeb, 0xe2, 0xdc, 0xe5, 0xee, 0xf4, 0xf7, 0xf7, 0xf7, 0xf4, 0xf4, 0xf7, 0xfa, 0xf4, 0xeb, 0xe8, 0xee, 0xee, 0xe8, 0xe8, 0xee, 0xfa, 0x06, 0x09, 0x00, 0xf7, 0xf4, 0xf7, 0xfa, 0xfd, 0x06, 0x0f, 0x06, 0xee, 0xe2, 0xe5, 0xf1, 0xf4, 0xf4, 0xf4, 0xfa, 0x00, 0x00, 0xfa, 0xf7, 0xfa, 0x00, 0x03, 0x00, 0x00, 0x03, 0x03, 0x03, 0x09, 0x0f, 0x0c, 0x00, 0xf4, 0xee, 0xf1, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xfd, 0x00, 0x06, 0x0c, 0x06, 0xfd, 0xfa, 0x00, 0x06, 0x06, 0xfa, 0xf7, 0x03, 0x0f, 0x0c, 0x00, 0x00, 0x09, 0x12, 0x0f, 0x03, 0x06, 0x0c, 0x0c, 0x03, 0xfa, 0xf7, 0xf1, 0xee, 0xf1, 0xfa, 0x03, 0x03, 0xfa, 0xee, 0xf1, 0x00, 0x0f, 0x12, 0x0f, 0x09, 0x06, 0x00, 0xf7, 0xf1, 0xee, 0xf4, 0xfd, 0xfd, 0xf1, +0xe5, 0xe8, 0xee, 0xf4, 0xf7, 0xf7, 0xf7, 0xf4, 0xf7, 0xfa, 0x00, 0x06, 0x09, 0x06, 0xfa, 0xee, 0xeb, 0xf4, 0x09, 0x15, 0x15, 0x0f, 0x06, 0x03, 0x00, 0x03, 0x03, 0x00, 0x03, 0x09, 0x09, 0x03, 0x00, 0xfa, 0xf7, 0xf4, 0xfa, 0x00, 0x06, 0x06, 0x03, 0x03, 0x00, 0xfd, 0xfd, 0x03, 0x0c, 0x09, 0x00, 0xfd, 0x03, 0x09, 0x06, 0x06, 0x0f, 0x15, 0x0f, 0xfd, 0xee, 0xeb, 0xf4, 0xfd, 0x06, 0x06, 0x03, 0x00, 0x00, 0xfd, 0x03, 0x06, 0x03, 0xfa, 0xf7, 0xfa, 0xf7, 0xf4, 0xeb, 0xeb, 0xfd, 0x12, 0x18, 0x09, 0xfa, 0xf7, 0xfa, 0x00, 0xfd, 0xf7, 0xf4, 0xf4, 0xf1, 0xf7, 0x00, 0x00, 0xf4, 0xe8, 0xe5, 0xeb, 0xf7, 0x03, 0x06, 0x00, 0xf7, 0xf1, 0xee, 0xf1, 0xf7, 0x00, 0xfd, 0xfa, 0xf7, 0xfa, 0xfa, 0xfd, 0x00, 0xfd, 0xf4, 0xee, 0xf4, 0xfd, 0xfd, 0xfa, 0xfa, 0xfd, 0x00, 0xfa, 0xf1, 0xf1, 0xf7, 0xfd, 0xfd, 0xfa, 0xfd, 0x00, 0xfa, 0xf1, 0xeb, 0xf1, 0xf7, 0xf7, 0xf4, 0xf4, 0xf4, 0xfa, 0x00, 0x00, 0xfd, 0xf7, 0xf7, 0xfa, 0xfa, 0xfd, 0xfd, 0xfa, 0xfa, 0xfd, 0x00, 0xf7, 0xee, 0xe8, 0xe8, 0xeb, 0xf1, 0xf7, 0xfa, 0xf7, 0xeb, 0xe8, 0xee, 0xf4, 0xf4, 0xf4, 0xf4, 0xf7, 0xfa, 0xfa, 0xf7, 0xfd, 0x06, 0x09, 0x00, 0xf4, 0xee, 0xf1, 0xfd, 0x0f, 0x15, 0x0f, 0x06, 0x03, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x06, 0x0f, 0x15, 0x12, 0x0c, 0x0c, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x12, 0x0f, 0x09, 0x00, 0xfd, 0x03, 0x09, 0x0c, 0x0c, 0x09, 0x0f, 0x15, 0x12, 0x09, 0x06, 0x0c, 0x15, 0x12, 0x0f, 0x0f, 0x0c, 0x0f, 0x0f, 0x09, 0x00, 0xfd, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x09, 0x06, 0x06, 0x06, 0x09, 0x0c, 0x09, 0x09, 0x06, 0x06, 0x06, 0x09, 0x0c, 0x12, 0x12, +0x06, 0xfa, 0xfd, 0x06, 0x09, 0x00, 0xfa, 0xfa, 0xfd, 0x06, 0x0c, 0x0c, 0x06, 0x00, 0xfd, 0x03, 0x06, 0x06, 0x03, 0x00, 0xfd, 0xfd, 0x00, 0xfd, 0x00, 0x06, 0x06, 0x00, 0x00, 0x00, 0xfd, 0xf1, 0xf1, 0xfd, 0x0c, 0x12, 0x09, 0x09, 0x0f, 0x12, 0x0f, 0x0f, 0x0f, 0x0f, 0x06, 0xfd, 0xfd, 0xfd, 0xfd, 0xf7, 0xf7, 0xf4, 0xf7, 0xfd, 0xfa, 0xf4, 0xf1, 0xf4, 0xf7, 0xf7, 0xfd, 0x06, 0x09, 0x06, 0x06, 0x06, 0xfd, 0xee, 0xe8, 0xe8, 0xee, 0xf4, 0x00, 0x0c, 0x0f, 0x06, 0xf1, 0xe5, 0xe5, 0xee, 0xf7, 0xf7, 0xf4, 0xf4, 0xf7, 0x00, 0x09, 0x0c, 0x0c, 0x06, 0x03, 0x00, 0xfd, 0x00, 0x03, 0x03, 0x03, 0xfd, 0xfa, 0xf7, 0xfd, 0x03, 0x03, 0x03, 0xfd, 0xfa, 0xfd, 0x03, 0x06, 0xfd, 0xf4, 0xf1, 0x00, 0x0c, 0x0f, 0x06, 0x03, 0x06, 0x06, 0x06, 0x09, 0x09, 0x06, 0x06, 0x09, 0x09, 0x06, 0x03, 0x03, 0x03, 0x00, 0xfd, 0x06, 0x0c, 0x09, 0x03, 0x00, 0x03, 0x06, 0x03, 0x00, 0xfa, 0xf7, 0xfd, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x06, 0x03, 0xfd, 0xfd, 0x03, 0x06, 0x03, 0x03, 0x06, 0x06, 0x09, 0x0f, 0x0f, 0x06, 0xfd, 0xfd, 0x03, 0x06, 0x03, 0xfa, 0xf7, 0xf1, 0xf1, 0xf7, 0xfd, 0x00, 0xfd, 0xfa, 0xfd, 0x00, 0xfd, 0x00, 0x00, 0x00, 0xfd, 0xf7, 0xfa, 0x00, 0x03, 0x06, 0x09, 0x03, 0xfd, 0xf7, 0xf7, 0xfa, 0x03, 0x06, 0x03, 0xfd, 0xfd, 0xfd, 0x00, 0x06, 0x06, 0x00, 0x00, 0xfd, 0x00, 0x06, 0x09, 0x0c, 0x0f, 0x09, 0x03, 0xfa, 0xfa, 0xfd, 0x03, 0x03, 0x03, 0x03, 0x03, 0x0c, 0x18, 0x1b, 0x12, 0x03, 0xfa, 0x00, 0x09, 0x0f, 0x0f, 0x0c, 0x03, 0x00, 0x03, 0x0c, 0x0c, 0x09, 0x09, 0x06, 0x00, 0xf7, 0xfa, 0x00, 0x06, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x03, +0x09, 0x09, 0x03, 0xfd, 0xfa, 0xfa, 0x03, 0x09, 0x0c, 0x06, 0x00, 0xfa, 0xfa, 0xfd, 0xfd, 0xf7, 0xf4, 0xfa, 0x03, 0x06, 0x03, 0xfd, 0xfa, 0x00, 0x09, 0x0c, 0x06, 0x00, 0xfa, 0xfd, 0x00, 0x06, 0x06, 0xfd, 0xf7, 0xf7, 0xfd, 0x03, 0x06, 0x00, 0xfa, 0xf7, 0xfa, 0x00, 0x03, 0x03, 0x03, 0x06, 0x06, 0xfd, 0xf4, 0xf1, 0xf1, 0xf7, 0xfd, 0x03, 0x06, 0x03, 0xfd, 0xf4, 0xf1, 0xfa, 0x00, 0x03, 0x00, 0x00, 0xfd, 0xf7, 0xf4, 0xf7, 0xfa, 0x00, 0xfd, 0xfd, 0xfa, 0xf7, 0xf7, 0xfa, 0x00, 0x03, 0x00, 0xfd, 0xfd, 0xfa, 0xf4, 0xf4, 0xf4, 0xf7, 0xfa, 0xf7, 0xf1, 0xee, 0xeb, 0xee, 0xf4, 0xf7, 0xf4, 0xeb, 0xe8, 0xf4, 0xfd, 0x00, 0xfd, 0xf7, 0xf4, 0xf4, 0xf4, 0xf4, 0xf1, 0xee, 0xeb, 0xeb, 0xf4, 0xfa, 0xf4, 0xee, 0xe8, 0xeb, 0xee, 0xf4, 0xfa, 0xfd, 0xf7, 0xf1, 0xf1, 0xf1, 0xf1, 0xf4, 0xf7, 0xf7, 0xf1, 0xeb, 0xeb, 0xf1, 0xfa, 0x00, 0xfd, 0xf7, 0xf7, 0xfd, 0x00, 0xfd, 0xf4, 0xf1, 0xf1, 0xf4, 0xf1, 0xf4, 0xfa, 0xfd, 0xfa, 0xfa, 0x00, 0x03, 0xfd, 0xfd, 0x03, 0x09, 0x00, 0xf7, 0xf7, 0xfa, 0xfa, 0xf7, 0xf4, 0xf7, 0xfa, 0xfd, 0xf7, 0xf4, 0xfa, 0xfd, 0xfa, 0xf4, 0xf4, 0xfa, 0xfd, 0xfd, 0xf7, 0xfa, 0x00, 0x03, 0x00, 0xfa, 0xfa, 0xf7, 0xf7, 0xf4, 0xf1, 0xf4, 0xfd, 0x00, 0x00, 0xfa, 0xf7, 0xf7, 0xfa, 0xfa, 0xf7, 0xf4, 0xf4, 0xfa, 0xfd, 0x00, 0x00, 0xfd, 0xf4, 0xf1, 0xf4, 0xfa, 0xfd, 0x00, 0xfd, 0xfd, 0xfd, 0x00, 0x06, 0x06, 0x00, 0xfa, 0xfa, 0x00, 0x03, 0x00, 0xf7, 0xf1, 0xf4, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xfa, 0xfd, 0x00, 0xfd, 0xfa, 0xf4, 0xf4, 0xf4, 0xfa, 0x03, 0x06, 0x00, 0xfa, 0xfd, 0x06, 0x06, 0xfd, 0xf7, 0xf7, 0xfd, 0xfd, +0xfa, 0xf7, 0xf7, 0xf7, 0xf7, 0xfa, 0x03, 0x09, 0x09, 0x06, 0x00, 0xfa, 0xf4, 0xf7, 0xfd, 0x00, 0x00, 0xfd, 0xfd, 0xfd, 0xfa, 0xf7, 0xfd, 0x03, 0x09, 0x06, 0x00, 0xfa, 0xf7, 0xf7, 0xfd, 0xfd, 0xfd, 0xfa, 0xf7, 0xf4, 0xf7, 0xfa, 0x03, 0x03, 0xfd, 0xfd, 0x00, 0x06, 0x03, 0x00, 0xfd, 0xfd, 0xf7, 0xf1, 0xe8, 0xe8, 0xf1, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xf7, 0xf4, 0xf7, 0xf7, 0xf7, 0xf7, 0xfa, 0xfa, 0xf7, 0xf4, 0xf1, 0xee, 0xe8, 0xeb, 0xf4, 0xf4, 0xf1, 0xf4, 0xfd, 0x00, 0x00, 0x03, 0x06, 0x03, 0xfd, 0xf4, 0xf1, 0xf4, 0xfa, 0xfa, 0xf7, 0xf4, 0xf7, 0xfd, 0xfd, 0xf1, 0xe8, 0xe5, 0xeb, 0xf1, 0xf7, 0xf7, 0xf7, 0xf7, 0xf4, 0xf4, 0xf7, 0xfa, 0xf7, 0xf1, 0xee, 0xf4, 0xf7, 0xf4, 0xf4, 0xfa, 0x00, 0x03, 0x03, 0x06, 0x06, 0x06, 0x00, 0xfa, 0xfd, 0x00, 0x06, 0x06, 0x00, 0x00, 0x03, 0x06, 0x06, 0x00, 0xfa, 0xf7, 0xfa, 0x03, 0x09, 0x06, 0xfd, 0xf7, 0xfd, 0x03, 0x09, 0x0c, 0x09, 0x06, 0x06, 0x0c, 0x0c, 0x0c, 0x09, 0x06, 0x03, 0x03, 0x06, 0x0f, 0x12, 0x0f, 0x09, 0x09, 0x0c, 0x0f, 0x0f, 0x0f, 0x09, 0x00, 0x00, 0x03, 0x0c, 0x0f, 0x12, 0x0f, 0x0f, 0x12, 0x18, 0x1b, 0x12, 0x03, 0xfa, 0xfa, 0x00, 0x06, 0x0c, 0x12, 0x15, 0x15, 0x15, 0x0f, 0x03, 0xfd, 0x06, 0x12, 0x18, 0x15, 0x0c, 0x06, 0x00, 0x00, 0x06, 0x09, 0x0f, 0x15, 0x15, 0x0f, 0x09, 0x09, 0x0f, 0x18, 0x18, 0x12, 0x0c, 0x09, 0x09, 0x0c, 0x12, 0x15, 0x0f, 0x0c, 0x0c, 0x0c, 0x0f, 0x12, 0x0f, 0x0c, 0x09, 0x0c, 0x0f, 0x15, 0x18, 0x12, 0x0f, 0x0f, 0x12, 0x0f, 0x0c, 0x09, 0x09, 0x0f, 0x1b, 0x21, 0x1b, 0x12, 0x09, 0x09, 0x0c, 0x12, 0x12, 0x12, 0x12, 0x0c, 0x06, 0x09, 0x12, 0x15, +0x15, 0x0f, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x12, 0x15, 0x12, 0x06, 0x00, 0x03, 0x06, 0x0c, 0x0f, 0x0f, 0x06, 0xfd, 0xfd, 0x03, 0x09, 0x09, 0x09, 0x06, 0x09, 0x0c, 0x0c, 0x0c, 0x06, 0x03, 0x00, 0x00, 0xfd, 0x00, 0x00, 0xfd, 0xfd, 0x00, 0x00, 0x00, 0x03, 0x00, 0xfa, 0xf4, 0xf4, 0xfa, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x00, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfa, 0xfa, 0xfd, 0x03, 0x06, 0x03, 0xfd, 0xf7, 0xf4, 0xf4, 0xf4, 0xf4, 0xfa, 0x00, 0x03, 0x00, 0x00, 0xfd, 0x00, 0x03, 0x00, 0xfd, 0xfd, 0xfd, 0x00, 0x06, 0x09, 0x03, 0xfa, 0xf7, 0xfd, 0x06, 0x09, 0x06, 0x00, 0x00, 0x00, 0xfa, 0xf7, 0xfa, 0x00, 0x00, 0x00, 0xfa, 0xf4, 0xf1, 0xf7, 0xfa, 0xfd, 0xfa, 0xf7, 0xf4, 0xfa, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xfa, 0xfa, 0xfa, 0xfd, 0x00, 0x00, 0xfa, 0xf7, 0xf7, 0xfa, 0x00, 0x00, 0xfd, 0xfa, 0xfa, 0xfd, 0x00, 0x00, 0x00, 0xfd, 0xfa, 0xfd, 0x00, 0x00, 0xfd, 0x00, 0x03, 0x00, 0xfa, 0xf7, 0xfa, 0x00, 0x06, 0x06, 0x00, 0xfd, 0xfd, 0x00, 0x03, 0x03, 0x00, 0xfa, 0xf4, 0xf7, 0xf7, 0xf7, 0xfa, 0x00, 0x03, 0x00, 0xfa, 0xf7, 0xf4, 0xf4, 0xf7, 0xf7, 0xf7, 0xf7, 0xfd, 0x00, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x03, 0xfa, 0xf4, 0xf4, 0xf7, 0xf7, 0xfa, 0xfd, 0x03, 0x03, 0x00, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xf7, 0xf4, 0xf4, 0xfd, 0x00, 0xfa, 0xf7, 0xfa, 0xfd, 0xfd, 0xfd, 0xfa, 0xfa, 0xfa, 0xf7, 0xf7, 0xf4, 0xf7, 0xfd, 0x03, 0x03, 0x00, 0xfd, 0xf7, 0xf4, 0xf1, 0xf4, 0xfd, 0x00, 0xfd, 0xf4, 0xf1, 0xf1, 0xf4, 0xf7, 0xf7, 0xf4, 0xf4, 0xf1, 0xee, 0xeb, 0xeb, 0xee, 0xf1, 0xf4, 0xf7, 0xfa, 0xfa, 0xf7, +0xf4, 0xf7, 0xf7, 0xf4, 0xee, 0xeb, 0xeb, 0xee, 0xee, 0xee, 0xe8, 0xe5, 0xe8, 0xf1, 0xf4, 0xf4, 0xee, 0xe2, 0xdf, 0xe2, 0xe5, 0xe8, 0xeb, 0xf4, 0xf7, 0xf4, 0xeb, 0xe5, 0xe8, 0xeb, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xee, 0xeb, 0xe8, 0xe8, 0xeb, 0xf1, 0xee, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xee, 0xee, 0xeb, 0xe8, 0xe8, 0xee, 0xf1, 0xf1, 0xeb, 0xe5, 0xe2, 0xe8, 0xf1, 0xfa, 0xfd, 0xfd, 0xfa, 0xf4, 0xf4, 0xf1, 0xf1, 0xf1, 0xf4, 0xf4, 0xf4, 0xee, 0xee, 0xf1, 0xf7, 0xfd, 0xfa, 0xf7, 0xfa, 0xfd, 0x00, 0xfd, 0xfd, 0xfd, 0xfa, 0xf7, 0xfa, 0xfd, 0xfd, 0xfa, 0xf7, 0xf4, 0xf7, 0xf7, 0xf7, 0xfa, 0xfa, 0xf7, 0xf4, 0xf4, 0xfa, 0x00, 0x00, 0x00, 0x03, 0x03, 0x06, 0x09, 0x06, 0x03, 0x00, 0xfd, 0xfd, 0x00, 0x06, 0x09, 0x03, 0xfa, 0xfa, 0x00, 0x03, 0x06, 0x06, 0x00, 0xfd, 0xfd, 0xfd, 0xf7, 0xf4, 0xfa, 0x00, 0x06, 0x06, 0x03, 0x03, 0x06, 0x03, 0x03, 0x00, 0xfa, 0xf4, 0xf7, 0xfa, 0xf7, 0xf1, 0xf4, 0xf7, 0xfd, 0xfd, 0xf7, 0xf4, 0xf4, 0xf4, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xf7, 0xf1, 0xf1, 0xf1, 0xf1, 0xf4, 0xf4, 0xf4, 0xee, 0xeb, 0xee, 0xf1, 0xf4, 0xf4, 0xf4, 0xf7, 0xfa, 0xfd, 0xfd, 0xfa, 0xf4, 0xee, 0xee, 0xf1, 0xfa, 0x00, 0x00, 0xfa, 0xf7, 0xfa, 0xf7, 0xfd, 0x03, 0x06, 0x00, 0xf7, 0xfa, 0xfd, 0x00, 0x03, 0x03, 0x00, 0x00, 0xfd, 0xfd, 0x00, 0x00, 0x03, 0x03, 0x03, 0x00, 0x00, 0xfd, 0xfa, 0xfd, 0x03, 0x09, 0x09, 0x09, 0x09, 0x03, 0xfd, 0xfd, 0x03, 0x09, 0x09, 0x09, 0x06, 0x06, 0x09, 0x09, 0x09, 0x09, 0x0c, 0x09, 0x09, 0x09, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x06, 0x03, 0x03, 0x06, 0x09, 0x0f, 0x0f, 0x12, 0x15, 0x15, 0x15, 0x0f, 0x0c, +0x0c, 0x09, 0x09, 0x0c, 0x0c, 0x09, 0x09, 0x0c, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x12, 0x0f, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0c, 0x09, 0x0c, 0x12, 0x18, 0x1b, 0x1b, 0x1e, 0x1e, 0x1b, 0x15, 0x15, 0x18, 0x18, 0x12, 0x0c, 0x0f, 0x12, 0x18, 0x1b, 0x1b, 0x12, 0x0f, 0x0c, 0x09, 0x09, 0x0c, 0x0f, 0x0c, 0x06, 0x06, 0x09, 0x0c, 0x12, 0x18, 0x18, 0x12, 0x0c, 0x09, 0x06, 0x03, 0x03, 0x09, 0x0f, 0x12, 0x12, 0x0f, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x09, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x06, 0x06, 0x03, 0x06, 0x0c, 0x0f, 0x0c, 0x06, 0x03, 0xfd, 0xfd, 0xfd, 0x03, 0x06, 0x06, 0x06, 0x03, 0x03, 0x06, 0x06, 0x06, 0x03, 0x00, 0xfa, 0xf4, 0xf4, 0xfa, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x03, 0x03, 0x03, 0xfd, 0xfd, 0xfd, 0x00, 0xfd, 0xfa, 0xfa, 0xfa, 0xfa, 0xfd, 0x00, 0x03, 0x00, 0xfd, 0xfa, 0xfa, 0xfd, 0x03, 0x03, 0x03, 0x00, 0xfd, 0xfd, 0xfd, 0xfd, 0x00, 0x03, 0x09, 0x09, 0x09, 0x0c, 0x09, 0x09, 0x06, 0x03, 0x00, 0xfd, 0x00, 0x09, 0x12, 0x15, 0x15, 0x0c, 0x06, 0x03, 0x09, 0x0f, 0x12, 0x12, 0x0f, 0x0c, 0x0c, 0x0c, 0x0f, 0x15, 0x15, 0x12, 0x0c, 0x09, 0x09, 0x09, 0x0c, 0x0f, 0x0f, 0x0c, 0x09, 0x0c, 0x0f, 0x0f, 0x0c, 0x09, 0x0c, 0x12, 0x0f, 0x09, 0x06, 0x09, 0x0c, 0x0c, 0x09, 0x06, 0x06, 0x06, 0x09, 0x09, 0x0c, 0x12, 0x12, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x09, 0x09, 0x0c, 0x0f, 0x0f, 0x0f, 0x12, 0x0c, 0x06, 0x06, 0x09, 0x0f, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x03, 0x06, 0x09, 0x0c, 0x06, 0x03, 0x03, 0x09, 0x09, 0x06, 0x03, 0x03, 0x06, 0x09, 0x06, 0x03, 0x00, 0x03, 0x06, 0x06, 0x06, 0x09, +0x0c, 0x09, 0x03, 0x03, 0x06, 0x06, 0x03, 0x03, 0x06, 0x06, 0x00, 0xfa, 0xfa, 0xfd, 0x06, 0x09, 0x06, 0x03, 0x00, 0xfd, 0x00, 0x03, 0x06, 0x00, 0xfd, 0xfa, 0xf7, 0xfa, 0x00, 0x00, 0x00, 0xfa, 0xf7, 0xf7, 0xfa, 0xfa, 0xfd, 0xfd, 0xf7, 0xee, 0xe8, 0xe8, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xee, 0xee, 0xee, 0xeb, 0xe8, 0xeb, 0xeb, 0xe8, 0xe8, 0xe8, 0xeb, 0xeb, 0xeb, 0xe8, 0xe5, 0xe5, 0xe8, 0xe5, 0xe5, 0xe8, 0xee, 0xeb, 0xe2, 0xdc, 0xdf, 0xe5, 0xe8, 0xe2, 0xdf, 0xdc, 0xdc, 0xdf, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe5, 0xe2, 0xdf, 0xdc, 0xdf, 0xdf, 0xdc, 0xdc, 0xdc, 0xdf, 0xdf, 0xdc, 0xd9, 0xd9, 0xdc, 0xdc, 0xdc, 0xd9, 0xdc, 0xdc, 0xdc, 0xd9, 0xd3, 0xd3, 0xd9, 0xdf, 0xdf, 0xdf, 0xe2, 0xe2, 0xe2, 0xdc, 0xdc, 0xdf, 0xdf, 0xdf, 0xdf, 0xe2, 0xe5, 0xe5, 0xe5, 0xe8, 0xe8, 0xe2, 0xdc, 0xdc, 0xdf, 0xe2, 0xe5, 0xe8, 0xe8, 0xe8, 0xeb, 0xee, 0xee, 0xf1, 0xf1, 0xf1, 0xf1, 0xee, 0xee, 0xf1, 0xee, 0xee, 0xf4, 0xf7, 0xf7, 0xf4, 0xee, 0xeb, 0xeb, 0xee, 0xf4, 0xf7, 0xf7, 0xf7, 0xf4, 0xf1, 0xf1, 0xf4, 0xfa, 0xfd, 0xfa, 0xf7, 0xf4, 0xf7, 0xfa, 0xfd, 0xfd, 0xfa, 0xf7, 0xf4, 0xf4, 0xfa, 0xfd, 0x00, 0xfd, 0xfa, 0xfd, 0xfd, 0xfd, 0x00, 0x03, 0x06, 0x09, 0x09, 0x09, 0x09, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x03, 0x06, 0x0c, 0x0c, 0x0c, 0x09, 0x0c, 0x0c, 0x09, 0x06, 0x06, 0x03, 0x03, 0x06, 0x06, 0x06, 0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x09, 0x06, 0x03, 0x00, 0x03, 0x03, 0x03, 0x03, 0x06, 0x06, 0x06, 0x03, 0x00, 0x00, 0x03, 0x09, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0f, 0x0c, 0x0c, +0x09, 0x09, 0x0c, 0x0f, 0x0c, 0x09, 0x03, 0x03, 0x06, 0x09, 0x0c, 0x0c, 0x09, 0x09, 0x0c, 0x0f, 0x0f, 0x0f, 0x09, 0x03, 0x03, 0x06, 0x0c, 0x0c, 0x09, 0x09, 0x0c, 0x0f, 0x0f, 0x0c, 0x09, 0x09, 0x03, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x03, 0x03, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0xfd, 0xfd, 0xfd, 0xfd, 0xfa, 0xf7, 0xf7, 0xfa, 0xfd, 0xfd, 0xfa, 0xfa, 0xfd, 0xfd, 0xfd, 0xfa, 0xfa, 0xfd, 0xfd, 0xfd, 0xfa, 0xf7, 0xfa, 0xfd, 0xfa, 0xf7, 0xf1, 0xf1, 0xee, 0xee, 0xf1, 0xf4, 0xf4, 0xf4, 0xf4, 0xf7, 0xfa, 0xfa, 0xfa, 0xfd, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0xfd, 0xfa, 0xf7, 0xfa, 0x00, 0x03, 0x00, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0x03, 0x06, 0x06, 0x03, 0x06, 0x09, 0x0f, 0x12, 0x0f, 0x09, 0x0c, 0x0f, 0x12, 0x12, 0x0f, 0x0c, 0x09, 0x0c, 0x0c, 0x0c, 0x09, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0f, 0x15, 0x15, 0x15, 0x18, 0x18, 0x15, 0x18, 0x1e, 0x1e, 0x1e, 0x1b, 0x1b, 0x1b, 0x18, 0x12, 0x12, 0x15, 0x15, 0x12, 0x12, 0x15, 0x15, 0x15, 0x15, 0x18, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x15, 0x12, 0x0f, 0x0f, 0x12, 0x18, 0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x1b, 0x1b, 0x18, 0x15, 0x15, 0x18, 0x18, 0x15, 0x0f, 0x0f, 0x12, 0x15, 0x15, 0x12, 0x0f, 0x0c, 0x0f, 0x12, 0x12, 0x12, 0x12, 0x12, 0x0f, 0x0c, 0x0c, 0x0f, 0x15, 0x18, 0x15, 0x15, 0x18, 0x18, 0x15, 0x12, 0x0f, 0x0f, 0x0c, 0x0f, 0x0f, 0x0c, 0x09, 0x06, 0x06, 0x09, 0x0c, 0x09, 0x06, 0x03, 0x06, 0x09, 0x0c, 0x09, 0x06, 0x03, 0x03, 0x06, 0x06, 0x06, 0x09, 0x0c, 0x0f, 0x0c, 0x09, 0x06, 0x00, +0x00, 0x00, 0x00, 0x03, 0x06, 0x06, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x03, 0x09, 0x09, 0x06, 0x03, 0x03, 0x00, 0x00, 0xfd, 0xf7, 0xf4, 0xf4, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xfa, 0xfa, 0xf7, 0xf4, 0xf1, 0xf1, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf1, 0xf1, 0xf4, 0xf4, 0xf4, 0xf4, 0xee, 0xeb, 0xee, 0xee, 0xeb, 0xe5, 0xe8, 0xeb, 0xee, 0xf1, 0xf1, 0xeb, 0xeb, 0xeb, 0xeb, 0xe8, 0xe5, 0xe2, 0xe5, 0xeb, 0xee, 0xf1, 0xee, 0xeb, 0xeb, 0xeb, 0xee, 0xee, 0xee, 0xeb, 0xe8, 0xe5, 0xe5, 0xe8, 0xeb, 0xe8, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe2, 0xdf, 0xdf, 0xe2, 0xe2, 0xe2, 0xe5, 0xe2, 0xdc, 0xdc, 0xe2, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xeb, 0xee, 0xf1, 0xf1, 0xee, 0xe8, 0xe5, 0xe5, 0xe5, 0xe8, 0xe8, 0xeb, 0xee, 0xee, 0xeb, 0xe8, 0xe5, 0xe8, 0xeb, 0xeb, 0xe8, 0xeb, 0xf1, 0xf1, 0xf1, 0xeb, 0xeb, 0xeb, 0xeb, 0xee, 0xf1, 0xf1, 0xf1, 0xf1, 0xf4, 0xf4, 0xf4, 0xf1, 0xee, 0xeb, 0xeb, 0xee, 0xf1, 0xf4, 0xf1, 0xee, 0xeb, 0xeb, 0xeb, 0xee, 0xf1, 0xf1, 0xf1, 0xee, 0xee, 0xeb, 0xeb, 0xf1, 0xf4, 0xf7, 0xf7, 0xf4, 0xee, 0xeb, 0xee, 0xf1, 0xf1, 0xf1, 0xf1, 0xf4, 0xf4, 0xf1, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xf1, 0xf1, 0xee, 0xee, 0xf1, 0xf4, 0xf7, 0xfa, 0xfa, 0xfa, 0xfa, 0xf7, 0xf7, 0xf7, 0xfa, 0xfa, 0xfd, 0xfd, 0xfa, 0xfa, 0xfd, 0xfd, 0xfa, 0xf4, 0xf1, 0xf4, 0xf4, 0xf7, 0xfa, 0xfd, 0xfd, 0xfd, 0xfd, 0xfa, 0xf7, 0xfa, 0xfa, 0xfd, 0xfd, 0xfd, 0xfa, 0xf7, 0xf7, 0xfa, 0xfd, 0xfd, 0xfa, 0xf7, 0xfa, 0xfd, 0x00, 0xfd, 0x00, 0x03, 0x03, 0x00, 0xfd, 0xfa, 0xfd, 0xfd, 0xfa, 0xf4, 0xf4, 0xf7, 0xfa, 0xfa, 0x00, 0x06, 0x09, 0x06, 0x03, +0xfd, 0xfd, 0xfd, 0x00, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfa, 0xf7, 0xfa, 0xfa, 0xfa, 0xf7, 0xfa, 0xfd, 0x00, 0x03, 0x03, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0xfd, 0x00, 0x03, 0x03, 0x00, 0xfd, 0xfa, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xfd, 0xfd, 0x00, 0x06, 0x0c, 0x0c, 0x09, 0x06, 0x03, 0x03, 0x06, 0x06, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x06, 0x06, 0x09, 0x0c, 0x0c, 0x09, 0x09, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x03, 0xfd, 0xfd, 0x00, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x03, 0x06, 0x09, 0x06, 0x06, 0x06, 0x09, 0x0c, 0x0f, 0x0f, 0x0c, 0x0c, 0x09, 0x09, 0x06, 0x06, 0x06, 0x03, 0x03, 0x03, 0x06, 0x09, 0x09, 0x0c, 0x0c, 0x0f, 0x15, 0x15, 0x12, 0x12, 0x12, 0x15, 0x15, 0x12, 0x0f, 0x09, 0x09, 0x09, 0x0c, 0x0c, 0x09, 0x09, 0x0c, 0x0f, 0x0f, 0x0c, 0x09, 0x09, 0x03, 0x00, 0x00, 0x03, 0x09, 0x0c, 0x0f, 0x12, 0x12, 0x0f, 0x0c, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0c, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xf9, 0x05, 0x6d, 0xea, 0x80, 0x8a, 0x80, 0x15, 0x7f, 0x6e, 0x7f, 0x1e, 0x80, 0x3f, 0x7f, 0x6b, 0x7f, 0xb6, 0x80, 0x80, 0xcc, 0x0b, 0xda, 0xbf, 0x80, 0x88, 0x80, 0x85, 0x80, 0x83, 0x80, 0x82, 0x80, 0x82, 0x80, 0x81, 0x80, 0x81, +0x80, 0x80, 0x81, 0x80, 0x80, 0x80, 0x80, 0x8c, 0xa6, 0xa4, 0xb1, 0xb2, 0x94, 0xb8, 0xe4, 0xd6, 0xfb, 0x0d, 0x13, 0x27, 0x3f, 0x46, 0x56, 0x64, 0x6d, 0x7c, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x70, 0x6c, 0x58, 0x51, 0x44, 0x42, 0x38, 0x2e, 0x19, 0x12, 0x02, 0x07, 0xdc, 0xdf, 0xd2, 0xc4, 0xc9, 0xac, 0xae, 0x91, 0x97, 0x89, 0x87, 0x80, 0x81, 0x80, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x80, 0x82, 0x80, 0x8b, 0x8d, 0x97, 0x9e, 0x9f, 0xaa, 0xb0, 0xbb, 0xc3, 0xc8, 0xc9, 0xef, 0xf3, 0xed, 0xfb, 0x11, 0x1f, 0x22, 0x2c, 0x37, 0x43, 0x51, 0x52, 0x61, 0x6b, 0x69, 0x71, 0x7e, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7b, 0x7f, 0x7c, 0x6b, 0x6a, 0x61, 0x5f, 0x53, 0x4d, 0x3f, 0x3d, 0x2f, 0x28, 0x1c, 0x16, 0x0d, 0x00, 0xfc, 0xf9, 0xeb, 0xd4, 0xd0, 0xc3, 0xc4, 0xb5, 0xad, 0xad, 0xa0, 0x9f, 0x8e, 0x93, 0x8f, 0x82, 0x88, 0x84, 0x80, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x87, 0x81, 0x8a, 0x81, 0x8d, 0x86, 0x96, 0x91, 0xa7, 0xac, 0xac, 0xb8, 0xbe, 0xcb, 0xce, 0xd9, 0xde, 0xe3, 0xef, 0xfe, 0x03, 0x13, 0x10, 0x1d, 0x23, 0x32, 0x3c, 0x42, 0x41, 0x53, 0x61, 0x57, 0x69, 0x6e, 0x68, 0x7a, 0x75, 0x7d, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7d, 0x7a, 0x7a, 0x72, 0x6c, 0x6b, 0x65, 0x5a, 0x52, 0x49, +0x47, 0x3d, 0x3a, 0x31, 0x28, 0x1d, 0x0c, 0x0f, 0x02, 0x00, 0xf0, 0xe6, 0xe9, 0xd7, 0xd9, 0xcc, 0xc4, 0xb0, 0xb1, 0xaf, 0x98, 0xa4, 0x97, 0x8b, 0x8b, 0x89, 0x86, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x85, 0x88, 0x8c, 0x8f, 0x8e, 0x96, 0x97, 0xa2, 0xae, 0xb2, 0xb7, 0xc0, 0xc2, 0xca, 0xdc, 0xd7, 0xe6, 0xec, 0xf3, 0xf7, 0x01, 0x0b, 0x1a, 0x23, 0x26, 0x33, 0x36, 0x36, 0x3d, 0x44, 0x4f, 0x53, 0x5e, 0x66, 0x5e, 0x6a, 0x6f, 0x76, 0x77, 0x76, 0x78, 0x7f, 0x7f, 0x7d, 0x7f, 0x7f, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7c, 0x7d, 0x7e, 0x79, 0x76, 0x71, 0x73, 0x6c, 0x66, 0x67, 0x5d, 0x5b, 0x53, 0x48, 0x41, 0x3b, 0x34, 0x34, 0x31, 0x26, 0x1a, 0x11, 0x0b, 0x00, 0xfe, 0xf9, 0xea, 0xe6, 0xdf, 0xd4, 0xd2, 0xbf, 0xc3, 0xba, 0xad, 0xaa, 0xa4, 0xa2, 0x99, 0x9a, 0x8f, 0x8f, 0x89, 0x81, 0x84, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x80, 0x81, 0x80, 0x81, 0x80, 0x81, 0x81, 0x86, 0x84, 0x86, 0x91, 0x8e, 0x99, 0x9f, 0x9b, 0xa1, 0xaa, 0xb2, 0xbb, 0xbb, 0xc5, 0xc7, 0xd8, 0xd4, 0xdd, 0xec, 0xee, 0xf9, 0xfe, 0x06, 0x12, 0x10, 0x1a, 0x1f, 0x26, 0x36, 0x37, 0x39, 0x45, 0x43, 0x53, 0x5a, 0x5a, 0x61, 0x65, 0x70, 0x6e, 0x71, 0x75, 0x78, 0x7c, 0x7e, 0x7f, 0x7d, 0x7e, 0x7f, 0x7f, 0x7f, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7d, 0x7d, 0x7e, 0x7e, 0x73, 0x74, 0x74, 0x75, 0x64, 0x63, 0x66, 0x5c, 0x53, 0x4f, 0x48, 0x44, 0x40, 0x37, 0x34, 0x2b, 0x22, 0x1f, 0x12, 0x11, 0x08, 0x02, 0xfa, 0xf3, 0xee, 0xe6, 0xe4, 0xd6, 0xd6, 0xc6, 0xc2, 0xc0, +0xb9, 0xb4, 0xaa, 0xac, 0xa7, 0x98, 0x9d, 0x94, 0x92, 0x8f, 0x90, 0x8c, 0x81, 0x86, 0x82, 0x83, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x84, 0x80, 0x82, 0x82, 0x8a, 0x89, 0x8c, 0x90, 0x91, 0x9a, 0x99, 0x9e, 0x9e, 0xaa, 0xad, 0xb4, 0xbb, 0xb9, 0xc6, 0xd0, 0xd3, 0xda, 0xde, 0xe6, 0xec, 0xf4, 0xfa, 0x00, 0x04, 0x0e, 0x0e, 0x1a, 0x1e, 0x29, 0x2c, 0x31, 0x36, 0x3a, 0x48, 0x46, 0x4f, 0x53, 0x5a, 0x61, 0x68, 0x66, 0x6f, 0x73, 0x72, 0x78, 0x79, 0x7d, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7e, 0x78, 0x7a, 0x79, 0x76, 0x6e, 0x6b, 0x65, 0x5c, 0x5a, 0x5a, 0x54, 0x53, 0x4b, 0x46, 0x3e, 0x39, 0x33, 0x31, 0x28, 0x28, 0x17, 0x11, 0x0b, 0x09, 0xff, 0xf8, 0xf5, 0xec, 0xe7, 0xe0, 0xdf, 0xd5, 0xcd, 0xc9, 0xc7, 0xbd, 0xba, 0xb4, 0xb2, 0xa9, 0xa5, 0x9f, 0xa4, 0x96, 0x95, 0x8e, 0x90, 0x8b, 0x87, 0x88, 0x80, 0x84, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x80, 0x81, 0x82, 0x82, 0x84, 0x85, 0x86, 0x8c, 0x8d, 0x95, 0x95, 0x9c, 0xa2, 0xa3, 0xa6, 0xac, 0xb0, 0xb4, 0xbb, 0xc1, 0xc3, 0xc8, 0xd2, 0xd5, 0xdd, 0xe0, 0xe7, 0xe9, 0xf1, 0xfb, 0x00, 0x0a, 0x0b, 0x10, 0x17, 0x1d, 0x25, 0x29, 0x2a, 0x31, 0x3b, 0x3d, 0x45, 0x47, 0x4f, 0x54, 0x56, 0x5a, 0x5d, 0x62, 0x67, 0x6a, 0x6b, 0x74, 0x73, 0x76, 0x77, 0x7a, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7d, 0x7c, 0x7e, 0x7a, 0x7a, 0x73, 0x70, 0x6e, 0x6b, 0x69, 0x64, 0x63, 0x5b, 0x56, 0x53, 0x4e, +0x49, 0x43, 0x42, 0x3c, 0x34, 0x32, 0x2a, 0x22, 0x21, 0x19, 0x13, 0x0f, 0x0a, 0x05, 0xff, 0xf5, 0xf0, 0xeb, 0xe5, 0xe4, 0xe0, 0xd8, 0xd2, 0xcb, 0xc4, 0xc3, 0xbb, 0xb5, 0xb4, 0xad, 0xab, 0xa7, 0xa2, 0x9e, 0x99, 0x97, 0x92, 0x92, 0x8e, 0x87, 0x86, 0x83, 0x82, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x82, 0x85, 0x88, 0x85, 0x8d, 0x8c, 0x90, 0x95, 0x98, 0x9d, 0x9e, 0xa2, 0xa6, 0xa9, 0xb1, 0xb3, 0xb5, 0xc0, 0xc1, 0xca, 0xcf, 0xd4, 0xd6, 0xdc, 0xde, 0xe7, 0xea, 0xf0, 0xf7, 0xfc, 0xfd, 0x07, 0x0d, 0x11, 0x19, 0x1d, 0x20, 0x28, 0x2a, 0x2f, 0x37, 0x3a, 0x41, 0x45, 0x4a, 0x4e, 0x54, 0x54, 0x59, 0x61, 0x65, 0x65, 0x67, 0x6a, 0x6e, 0x73, 0x78, 0x76, 0x7d, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7b, 0x78, 0x78, 0x75, 0x73, 0x73, 0x6f, 0x6a, 0x69, 0x63, 0x5e, 0x5d, 0x57, 0x54, 0x4f, 0x49, 0x48, 0x46, 0x41, 0x3c, 0x34, 0x31, 0x2a, 0x26, 0x23, 0x1c, 0x15, 0x12, 0x10, 0x09, 0x02, 0xfa, 0xf9, 0xf4, 0xef, 0xec, 0xe4, 0xe0, 0xd9, 0xd4, 0xd2, 0xcc, 0xc9, 0xc4, 0xc0, 0xba, 0xb5, 0xb4, 0xae, 0xa8, 0xa6, 0xa5, 0xa0, 0x98, 0x99, 0x95, 0x8f, 0x8f, 0x8d, 0x8a, 0x85, 0x84, 0x84, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x83, 0x87, 0x88, 0x8a, 0x8e, 0x92, 0x94, 0x94, 0x96, 0x9c, 0x9f, 0xa3, 0xa7, 0xab, 0xaf, 0xb1, 0xb4, 0xbb, 0xc0, 0xc2, 0xc7, 0xcb, 0xd1, 0xd5, 0xd9, 0xdb, +0xe3, 0xe6, 0xeb, 0xf3, 0xf6, 0xf9, 0x01, 0x05, 0x07, 0x0c, 0x14, 0x18, 0x1b, 0x21, 0x24, 0x2c, 0x30, 0x36, 0x37, 0x3d, 0x41, 0x46, 0x4a, 0x4e, 0x51, 0x53, 0x59, 0x5b, 0x62, 0x64, 0x68, 0x69, 0x6c, 0x70, 0x71, 0x73, 0x78, 0x7a, 0x7b, 0x7f, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7e, 0x7f, 0x7b, 0x7a, 0x77, 0x75, 0x72, 0x70, 0x70, 0x6a, 0x66, 0x63, 0x62, 0x5f, 0x5a, 0x56, 0x53, 0x4f, 0x4b, 0x47, 0x45, 0x3d, 0x3a, 0x38, 0x33, 0x2f, 0x2b, 0x25, 0x1f, 0x1b, 0x16, 0x13, 0x0f, 0x0a, 0x04, 0xff, 0xfc, 0xf8, 0xf3, 0xee, 0xe8, 0xe5, 0xdf, 0xda, 0xd9, 0xd4, 0xcd, 0xcb, 0xc4, 0xc4, 0xbe, 0xba, 0xb7, 0xaf, 0xb0, 0xac, 0xa7, 0xa3, 0x9e, 0x9c, 0x9b, 0x98, 0x92, 0x91, 0x8d, 0x89, 0x88, 0x87, 0x84, 0x83, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x83, 0x84, 0x85, 0x87, 0x88, 0x8a, 0x8e, 0x90, 0x94, 0x96, 0x99, 0x9e, 0x9f, 0xa1, 0xa7, 0xa8, 0xac, 0xaf, 0xb3, 0xb7, 0xbb, 0xbe, 0xc2, 0xc7, 0xcb, 0xd0, 0xd3, 0xd8, 0xda, 0xe1, 0xe4, 0xe7, 0xed, 0xf2, 0xf7, 0xfb, 0xfe, 0x01, 0x07, 0x0c, 0x10, 0x13, 0x18, 0x1d, 0x21, 0x26, 0x28, 0x2d, 0x32, 0x37, 0x3a, 0x3e, 0x43, 0x44, 0x4a, 0x4c, 0x50, 0x54, 0x57, 0x5c, 0x5e, 0x61, 0x64, 0x67, 0x6a, 0x6e, 0x70, 0x72, 0x75, 0x76, 0x79, 0x7a, 0x7d, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7e, +0x7e, 0x7b, 0x79, 0x77, 0x76, 0x73, 0x71, 0x70, 0x6d, 0x69, 0x68, 0x64, 0x62, 0x5f, 0x5c, 0x5a, 0x55, 0x53, 0x4f, 0x4b, 0x49, 0x45, 0x40, 0x3d, 0x39, 0x36, 0x32, 0x2d, 0x2a, 0x26, 0x22, 0x1e, 0x1b, 0x16, 0x12, 0x0d, 0x0a, 0x06, 0x01, 0xfd, 0xf8, 0xf4, 0xf1, 0xec, 0xe8, 0xe4, 0xdf, 0xdb, 0xd8, 0xd3, 0xd1, 0xcc, 0xc8, 0xc5, 0xc1, 0xbd, 0xba, 0xb7, 0xb3, 0xb0, 0xac, 0xa9, 0xa7, 0xa3, 0x9f, 0x9e, 0x9a, 0x98, 0x94, 0x93, 0x90, 0x8e, 0x8b, 0x88, 0x87, 0x85, 0x84, 0x82, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x83, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x91, 0x93, 0x95, 0x97, 0x99, 0x9d, 0x9f, 0xa2, 0xa5, 0xa8, 0xab, 0xaf, 0xb1, 0xb4, 0xb8, 0xbb, 0xbf, 0xc2, 0xc5, 0xc9, 0xce, 0xd1, 0xd4, 0xd8, 0xdc, 0xe0, 0xe4, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfa, 0xff, 0x03, 0x07, 0x0b, 0x0f, 0x12, 0x16, 0x1a, 0x1e, 0x22, 0x26, 0x29, 0x2d, 0x31, 0x35, 0x38, 0x3c, 0x40, 0x43, 0x47, 0x49, 0x4e, 0x51, 0x53, 0x56, 0x5a, 0x5d, 0x60, 0x62, 0x65, 0x68, 0x6a, 0x6d, 0x6f, 0x71, 0x74, 0x76, 0x78, 0x79, 0x7b, 0x7d, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7d, 0x7c, 0x7a, 0x79, 0x77, 0x75, 0x73, 0x71, 0x6f, 0x6d, 0x6b, 0x68, 0x66, 0x63, 0x61, 0x5e, 0x5b, 0x59, 0x56, 0x53, 0x50, 0x4d, 0x4a, 0x47, 0x43, 0x40, 0x3d, 0x39, 0x36, 0x33, 0x2f, 0x2c, 0x28, 0x24, 0x21, 0x1d, 0x1a, 0x16, 0x12, 0x0f, 0x0b, 0x07, 0x03, +0x00, 0xfc, 0xf8, 0xf4, 0xf1, 0xed, 0xe9, 0xe6, 0xe2, 0xde, 0xda, 0xd7, 0xd3, 0xd0, 0xcc, 0xc9, 0xc5, 0xc2, 0xbe, 0xbb, 0xb8, 0xb5, 0xb1, 0xae, 0xab, 0xa8, 0xa5, 0xa3, 0xa0, 0x9d, 0x9b, 0x98, 0x96, 0x93, 0x91, 0x8f, 0x8d, 0x8a, 0x89, 0x87, 0x85, 0x83, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x82, 0x83, 0x84, 0x86, 0x87, 0x89, 0x8b, 0x8d, 0x8f, 0x91, 0x93, 0x95, 0x97, 0x9a, 0x9c, 0x9e, 0xa1, 0xa3, 0xa6, 0xa9, 0xab, 0xae, 0xb1, 0xb4, 0xb7, 0xba, 0xbd, 0xc0, 0xc3, 0xc7, 0xca, 0xcd, 0xd0, 0xd4, 0xd7, 0xda, 0xde, 0xe1, 0xe5, 0xe8, 0xec, 0xef, 0xf3, 0xf6, 0xfa, 0xfe, 0x01, 0x05, 0x08, 0x0c, 0x0f, 0x13, 0x17, 0x1a, 0x1e, 0x21, 0x25, 0x28, 0x2b, 0x2f, 0x32, 0x36, 0x39, 0x3c, 0x3f, 0x43, 0x46, 0x49, 0x4c, 0x4f, 0x52, 0x55, 0x57, 0x5a, 0x5d, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7b, 0x7d, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7e, 0x7d, 0x7b, 0x7a, 0x78, 0x77, 0x75, 0x74, 0x72, 0x70, 0x6e, 0x6c, 0x6a, 0x68, 0x66, 0x63, 0x61, 0x5f, 0x5c, 0x5a, 0x57, 0x55, 0x52, 0x4f, 0x4d, 0x4a, 0x47, 0x44, 0x41, 0x3e, 0x3b, 0x38, 0x35, 0x32, 0x2f, 0x2c, 0x28, 0x25, 0x22, 0x1f, 0x1b, 0x18, 0x15, 0x11, 0x0e, 0x0b, 0x07, 0x04, 0x01, 0xfd, 0xfa, 0xf6, 0xf3, 0xf0, 0xec, 0xe9, 0xe6, 0xe2, 0xdf, 0xdc, 0xd8, 0xd5, 0xd2, +0xcf, 0xcb, 0xc8, 0xc5, 0xc2, 0xbf, 0xbc, 0xb9, 0xb6, 0xb3, 0xb0, 0xae, 0xab, 0xa8, 0xa6, 0xa3, 0xa0, 0x9e, 0x9c, 0x99, 0x97, 0x95, 0x93, 0x91, 0x8f, 0x8d, 0x8b, 0x89, 0x87, 0x86, 0x84, 0x83, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x83, 0x84, 0x85, 0x86, 0x88, 0x89, 0x8b, 0x8d, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e, 0xa1, 0xa3, 0xa5, 0xa8, 0xaa, 0xad, 0xaf, 0xb2, 0xb4, 0xb7, 0xba, 0xbc, 0xbf, 0xc2, 0xc5, 0xc8, 0xcb, 0xce, 0xd1, 0xd4, 0xd7, 0xda, 0xdd, 0xe0, 0xe3, 0xe6, 0xea, 0xed, 0xf0, 0xf3, 0xf6, 0xfa, 0xfd, 0x00, 0x03, 0x07, 0x0a, 0x0d, 0x10, 0x13, 0x17, 0x1a, 0x1d, 0x20, 0x23, 0x27, 0x2a, 0x2d, 0x30, 0x33, 0x36, 0x39, 0x3c, 0x3f, 0x42, 0x45, 0x47, 0x4a, 0x4d, 0x50, 0x52, 0x55, 0x58, 0x5a, 0x5d, 0x5f, 0x61, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7d, 0x7c, 0x7b, 0x7a, 0x78, 0x77, 0x76, 0x74, 0x73, 0x71, 0x6f, 0x6d, 0x6c, 0x6a, 0x68, 0x66, 0x64, 0x62, 0x60, 0x5d, 0x5b, 0x59, 0x57, 0x54, 0x52, 0x4f, 0x4d, 0x4a, 0x48, 0x45, 0x43, 0x40, 0x3d, 0x3b, 0x38, 0x35, 0x32, 0x2f, 0x2c, 0x2a, 0x27, 0x24, 0x21, 0x1e, 0x1b, 0x18, 0x15, 0x12, 0x0f, 0x0c, 0x08, 0x05, 0x02, 0xff, 0xfc, 0xf9, 0xf6, 0xf3, +0xf0, 0xed, 0xea, 0xe6, 0xe3, 0xe0, 0xdd, 0xda, 0xd7, 0xd4, 0xd1, 0xce, 0xcb, 0xc9, 0xc6, 0xc3, 0xc0, 0xbd, 0xbb, 0xb8, 0xb5, 0xb3, 0xb0, 0xad, 0xab, 0xa8, 0xa6, 0xa4, 0xa1, 0x9f, 0x9d, 0x9b, 0x98, 0x96, 0x94, 0x92, 0x90, 0x8f, 0x8d, 0x8b, 0x89, 0x88, 0x86, 0x85, 0x83, 0x82, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8b, 0x8c, 0x8e, 0x90, 0x91, 0x93, 0x95, 0x97, 0x98, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa5, 0xa7, 0xa9, 0xab, 0xae, 0xb0, 0xb2, 0xb5, 0xb7, 0xba, 0xbc, 0xbf, 0xc1, 0xc4, 0xc7, 0xc9, 0xcc, 0xcf, 0xd1, 0xd4, 0xd7, 0xda, 0xdd, 0xdf, 0xe2, 0xe5, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf7, 0xfa, 0xfd, 0x00, 0x03, 0x06, 0x09, 0x0c, 0x0f, 0x12, 0x14, 0x17, 0x1a, 0x1d, 0x20, 0x23, 0x26, 0x29, 0x2c, 0x2f, 0x32, 0x34, 0x37, 0x3a, 0x3d, 0x3f, 0x42, 0x45, 0x47, 0x4a, 0x4c, 0x4f, 0x51, 0x54, 0x56, 0x59, 0x5b, 0x5d, 0x5f, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x71, 0x73, 0x75, 0x77, 0x78, 0x7a, 0x7b, 0x7c, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7e, 0x7d, 0x7c, 0x7a, 0x79, 0x78, 0x77, 0x75, 0x74, 0x72, 0x71, 0x6f, 0x6e, 0x6c, 0x6a, 0x69, 0x67, 0x65, 0x63, 0x61, 0x5f, 0x5d, 0x5b, 0x59, 0x57, 0x55, 0x52, 0x50, +0x4e, 0x4c, 0x49, 0x47, 0x45, 0x42, 0x40, 0x3d, 0x3b, 0x38, 0x36, 0x33, 0x30, 0x2e, 0x2b, 0x28, 0x26, 0x23, 0x20, 0x1d, 0x1b, 0x18, 0x15, 0x12, 0x0f, 0x0d, 0x0a, 0x07, 0x04, 0x01, 0xfe, 0xfb, 0xf9, 0xf6, 0xf3, 0xf0, 0xed, 0xea, 0xe7, 0xe5, 0xe2, 0xdf, 0xdc, 0xd9, 0xd7, 0xd4, 0xd1, 0xce, 0xcc, 0xc9, 0xc6, 0xc4, 0xc1, 0xbe, 0xbc, 0xb9, 0xb7, 0xb4, 0xb2, 0xaf, 0xad, 0xab, 0xa8, 0xa6, 0xa4, 0xa2, 0x9f, 0x9d, 0x9b, 0x99, 0x97, 0x95, 0x93, 0x92, 0x90, 0x8e, 0x8c, 0x8b, 0x89, 0x88, 0x86, 0x85, 0x83, 0x82, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8a, 0x8c, 0x8d, 0x8f, 0x90, 0x92, 0x93, 0x95, 0x97, 0x99, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb3, 0xb5, 0xb7, 0xb9, 0xbc, 0xbe, 0xc1, 0xc3, 0xc5, 0xc8, 0xca, 0xcd, 0xcf, 0xd2, 0xd4, 0xd7, 0xda, 0xdc, 0xdf, 0xe2, 0xe4, 0xe7, 0xea, 0xec, 0xef, 0xf2, 0xf5, 0xf7, 0xfa, 0xfd, 0x00, 0x02, 0x05, 0x08, 0x0b, 0x0d, 0x10, 0x13, 0x16, 0x19, 0x1b, 0x1e, 0x21, 0x23, 0x26, 0x29, 0x2c, 0x2e, 0x31, 0x34, 0x36, 0x39, 0x3b, 0x3e, 0x40, 0x43, 0x45, 0x48, 0x4a, 0x4d, 0x4f, 0x51, 0x54, 0x56, 0x58, 0x5a, 0x5d, 0x5f, 0x61, 0x63, 0x65, 0x67, 0x69, 0x6b, 0x6c, 0x6e, 0x70, 0x72, 0x73, 0x75, 0x76, 0x78, 0x79, 0x7b, 0x7c, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, +0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7e, 0x7d, 0x7c, 0x7a, 0x79, 0x78, 0x77, 0x76, 0x74, 0x73, 0x72, 0x70, 0x6f, 0x6d, 0x6c, 0x6a, 0x68, 0x67, 0x65, 0x63, 0x61, 0x60, 0x5e, 0x5c, 0x5a, 0x58, 0x56, 0x54, 0x52, 0x50, 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x42, 0x40, 0x3e, 0x3b, 0x39, 0x37, 0x34, 0x32, 0x2f, 0x2d, 0x2a, 0x28, 0x25, 0x23, 0x20, 0x1e, 0x1b, 0x18, 0x16, 0x13, 0x11, 0x0e, 0x0b, 0x09, 0x06, 0x03, 0x01, 0xfe, 0xfb, 0xf9, 0xf6, 0xf3, 0xf1, 0xee, 0xeb, 0xe8, 0xe6, 0xe3, 0xe0, 0xde, 0xdb, 0xd9, 0xd6, 0xd3, 0xd1, 0xce, 0xcc, 0xc9, 0xc7, 0xc4, 0xc2, 0xbf, 0xbd, 0xba, 0xb8, 0xb6, 0xb3, 0xb1, 0xaf, 0xac, 0xaa, 0xa8, 0xa6, 0xa4, 0xa2, 0xa0, 0x9e, 0x9c, 0x9a, 0x98, 0x96, 0x94, 0x92, 0x91, 0x8f, 0x8d, 0x8c, 0x8a, 0x88, 0x87, 0x86, 0x84, 0x83, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8b, 0x8c, 0x8d, 0x8f, 0x90, 0x92, 0x93, 0x95, 0x96, 0x98, 0x9a, 0x9b, 0x9d, 0x9f, 0xa1, 0xa3, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, 0xbf, 0xc2, 0xc4, 0xc6, 0xc9, 0xcb, 0xcd, 0xd0, 0xd2, 0xd5, 0xd7, 0xd9, 0xdc, 0xde, 0xe1, 0xe3, 0xe6, 0xe8, 0xeb, 0xee, 0xf0, 0xf3, 0xf5, 0xf8, 0xfa, 0xfd, 0x00, 0x02, +0x05, 0x08, 0x0a, 0x0d, 0x0f, 0x12, 0x15, 0x17, 0x1a, 0x1c, 0x1f, 0x22, 0x24, 0x27, 0x29, 0x2c, 0x2e, 0x31, 0x33, 0x36, 0x38, 0x3b, 0x3d, 0x40, 0x42, 0x44, 0x47, 0x49, 0x4b, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5b, 0x5d, 0x5f, 0x61, 0x63, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x6f, 0x71, 0x73, 0x74, 0x76, 0x77, 0x79, 0x7a, 0x7b, 0x7d, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x78, 0x77, 0x76, 0x75, 0x74, 0x72, 0x71, 0x70, 0x6e, 0x6d, 0x6b, 0x6a, 0x68, 0x66, 0x65, 0x63, 0x61, 0x60, 0x5e, 0x5c, 0x5a, 0x58, 0x56, 0x55, 0x53, 0x51, 0x4f, 0x4d, 0x4a, 0x48, 0x46, 0x44, 0x42, 0x40, 0x3e, 0x3b, 0x39, 0x37, 0x35, 0x32, 0x30, 0x2e, 0x2b, 0x29, 0x27, 0x24, 0x22, 0x1f, 0x1d, 0x1a, 0x18, 0x15, 0x13, 0x10, 0x0e, 0x0b, 0x09, 0x06, 0x04, 0x01, 0xff, 0xfc, 0xfa, 0xf7, 0xf5, 0xf2, 0xef, 0xed, 0xea, 0xe8, 0xe5, 0xe3, 0xe0, 0xde, 0xdb, 0xd9, 0xd6, 0xd4, 0xd1, 0xcf, 0xcc, 0xca, 0xc8, 0xc5, 0xc3, 0xc0, 0xbe, 0xbc, 0xba, 0xb7, 0xb5, 0xb3, 0xb1, 0xae, 0xac, 0xaa, 0xa8, 0xa6, 0xa4, 0xa2, 0xa0, 0x9e, 0x9c, 0x9a, 0x98, 0x97, 0x95, 0x93, 0x91, 0x90, 0x8e, 0x8d, 0x8b, 0x8a, 0x88, 0x87, 0x85, 0x84, 0x83, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, +0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8b, 0x8c, 0x8d, 0x8f, 0x90, 0x91, 0x93, 0x94, 0x96, 0x97, 0x99, 0x9a, 0x9c, 0x9e, 0x9f, 0xa1, 0xa3, 0xa5, 0xa7, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe, 0xc1, 0xc3, 0xc5, 0xc7, 0xc9, 0xcc, 0xce, 0xd0, 0xd2, 0xd5, 0xd7, 0xd9, 0xdc, 0xde, 0xe0, 0xe3, 0xe5, 0xe8, 0xea, 0xed, 0xef, 0xf1, 0xf4, 0xf6, 0xf9, 0xfb, 0xfe, 0x00, 0x03, 0x05, 0x08, 0x0a, 0x0d, 0x0f, 0x12, 0x14, 0x17, 0x19, 0x1c, 0x1e, 0x21, 0x23, 0x26, 0x28, 0x2a, 0x2d, 0x2f, 0x32, 0x34, 0x36, 0x39, 0x3b, 0x3d, 0x40, 0x42, 0x44, 0x46, 0x49, 0x4b, 0x4d, 0x4f, 0x51, 0x53, 0x55, 0x57, 0x59, 0x5b, 0x5d, 0x5f, 0x61, 0x63, 0x65, 0x67, 0x69, 0x6a, 0x6c, 0x6e, 0x6f, 0x71, 0x72, 0x74, 0x75, 0x77, 0x78, 0x7a, 0x7b, 0x7c, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, 0x77, 0x76, 0x75, 0x73, 0x72, 0x71, 0x6f, 0x6e, 0x6d, 0x6b, 0x6a, 0x68, 0x67, 0x65, 0x64, 0x62, 0x60, 0x5f, 0x5d, 0x5b, 0x59, 0x58, 0x56, 0x54, 0x52, 0x50, 0x4e, 0x4c, 0x4a, 0x48, 0x46, 0x44, 0x42, 0x40, 0x3e, 0x3c, 0x3a, 0x38, 0x35, 0x33, 0x31, 0x2f, 0x2c, 0x2a, 0x28, 0x26, 0x23, 0x21, 0x1f, 0x1c, +0x1a, 0x18, 0x15, 0x13, 0x11, 0x0e, 0x0c, 0x09, 0x07, 0x04, 0x02, 0x00, 0xfd, 0xfb, 0xf8, 0xf6, 0xf3, 0xf1, 0xef, 0xec, 0xea, 0xe7, 0xe5, 0xe2, 0xe0, 0xde, 0xdb, 0xd9, 0xd6, 0xd4, 0xd2, 0xcf, 0xcd, 0xcb, 0xc8, 0xc6, 0xc4, 0xc2, 0xbf, 0xbd, 0xbb, 0xb9, 0xb7, 0xb4, 0xb2, 0xb0, 0xae, 0xac, 0xaa, 0xa8, 0xa6, 0xa4, 0xa2, 0xa0, 0x9e, 0x9d, 0x9b, 0x99, 0x97, 0x96, 0x94, 0x92, 0x91, 0x8f, 0x8e, 0x8c, 0x8b, 0x89, 0x88, 0x86, 0x85, 0x84, 0x83, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8e, 0x8f, 0x90, 0x92, 0x93, 0x94, 0x96, 0x97, 0x99, 0x9a, 0x9c, 0x9e, 0x9f, 0xa1, 0xa3, 0xa4, 0xa6, 0xa8, 0xaa, 0xab, 0xad, 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, 0xbf, 0xc1, 0xc3, 0xc5, 0xc7, 0xc9, 0xcb, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd9, 0xdb, 0xdd, 0xdf, 0xe2, 0xe4, 0xe6, 0xe9, 0xeb, 0xed, 0xf0, 0xf2, 0xf4, 0xf7, 0xf9, 0xfb, 0xfe, 0x00, 0x03, 0x05, 0x07, 0x0a, 0x0c, 0x0f, 0x11, 0x13, 0x16, 0x18, 0x1a, 0x1d, 0x1f, 0x22, 0x24, 0x26, 0x29, 0x2b, 0x2d, 0x30, 0x32, 0x34, 0x36, 0x39, 0x3b, 0x3d, 0x3f, 0x41, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x61, 0x63, 0x65, 0x67, 0x68, 0x6a, 0x6c, 0x6d, 0x6f, 0x70, 0x72, 0x73, 0x75, 0x76, 0x77, 0x79, 0x7a, 0x7b, +0x7c, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x71, 0x70, 0x6f, 0x6d, 0x6c, 0x6b, 0x69, 0x68, 0x66, 0x65, 0x63, 0x62, 0x60, 0x5f, 0x5d, 0x5b, 0x5a, 0x58, 0x56, 0x54, 0x52, 0x51, 0x4f, 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x39, 0x37, 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2a, 0x28, 0x26, 0x24, 0x22, 0x1f, 0x1d, 0x1b, 0x19, 0x16, 0x14, 0x12, 0x0f, 0x0d, 0x0b, 0x09, 0x06, 0x04, 0x02, 0xff, 0xfd, 0xfb, 0xf8, 0xf6, 0xf4, 0xf1, 0xef, 0xed, 0xea, 0xe8, 0xe6, 0xe3, 0xe1, 0xdf, 0xdc, 0xda, 0xd8, 0xd5, 0xd3, 0xd1, 0xcf, 0xcc, 0xca, 0xc8, 0xc6, 0xc4, 0xc2, 0xbf, 0xbd, 0xbb, 0xb9, 0xb7, 0xb5, 0xb3, 0xb1, 0xaf, 0xad, 0xab, 0xa9, 0xa7, 0xa5, 0xa4, 0xa2, 0xa0, 0x9e, 0x9d, 0x9b, 0x99, 0x98, 0x96, 0x94, 0x93, 0x91, 0x90, 0x8e, 0x8d, 0x8c, 0x8a, 0x89, 0x88, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8f, 0x90, 0x91, +0x92, 0x94, 0x95, 0x96, 0x98, 0x99, 0x9b, 0x9c, 0x9e, 0x9f, 0xa1, 0xa3, 0xa4, 0xa6, 0xa8, 0xa9, 0xab, 0xad, 0xaf, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe, 0xbf, 0xc1, 0xc3, 0xc5, 0xc7, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdd, 0xdf, 0xe1, 0xe3, 0xe5, 0xe8, 0xea, 0xec, 0xee, 0xf1, 0xf3, 0xf5, 0xf7, 0xfa, 0xfc, 0xfe, 0x00, 0x03, 0x05, 0x07, 0x0a, 0x0c, 0x0e, 0x10, 0x13, 0x15, 0x17, 0x1a, 0x1c, 0x1e, 0x20, 0x23, 0x25, 0x27, 0x29, 0x2b, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x39, 0x3b, 0x3d, 0x3f, 0x41, 0x43, 0x45, 0x47, 0x49, 0x4b, 0x4d, 0x4f, 0x51, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x5f, 0x61, 0x63, 0x64, 0x66, 0x68, 0x69, 0x6b, 0x6c, 0x6e, 0x6f, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7c, 0x7d, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7e, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x6f, 0x6e, 0x6d, 0x6c, 0x6a, 0x69, 0x68, 0x66, 0x65, 0x63, 0x62, 0x60, 0x5f, 0x5d, 0x5c, 0x5a, 0x58, 0x57, 0x55, 0x53, 0x52, 0x50, 0x4e, 0x4c, 0x4a, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x39, 0x37, 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1c, 0x1a, 0x18, 0x16, 0x14, 0x12, 0x0f, 0x0d, 0x0b, 0x09, 0x06, 0x04, 0x02, 0x00, 0xfe, 0xfb, 0xf9, 0xf7, 0xf5, 0xf2, 0xf0, +0xee, 0xec, 0xe9, 0xe7, 0xe5, 0xe3, 0xe1, 0xde, 0xdc, 0xda, 0xd8, 0xd6, 0xd4, 0xd1, 0xcf, 0xcd, 0xcb, 0xc9, 0xc7, 0xc5, 0xc3, 0xc1, 0xbf, 0xbd, 0xbb, 0xb9, 0xb7, 0xb5, 0xb3, 0xb1, 0xaf, 0xad, 0xac, 0xaa, 0xa8, 0xa6, 0xa5, 0xa3, 0xa1, 0xa0, 0x9e, 0x9c, 0x9b, 0x99, 0x98, 0x96, 0x95, 0x93, 0x92, 0x91, 0x8f, 0x8e, 0x8d, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x82, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x94, 0x95, 0x96, 0x98, 0x99, 0x9a, 0x9c, 0x9d, 0x9f, 0xa0, 0xa2, 0xa3, 0xa5, 0xa6, 0xa8, 0xa9, 0xab, 0xad, 0xae, 0xb0, 0xb2, 0xb4, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, 0xbe, 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4, 0xe7, 0xe9, 0xeb, 0xed, 0xef, 0xf1, 0xf3, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe, 0x00, 0x03, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1b, 0x1d, 0x1f, 0x21, 0x23, 0x25, 0x27, 0x29, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x40, 0x42, 0x44, 0x46, 0x47, 0x49, 0x4b, 0x4d, 0x4f, 0x51, 0x52, 0x54, 0x56, 0x58, 0x59, 0x5b, 0x5d, 0x5e, 0x60, 0x61, 0x63, 0x64, 0x66, 0x67, 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x70, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, +0x7b, 0x7c, 0x7d, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7e, 0x7d, 0x7d, 0x7c, 0x7b, 0x7a, 0x7a, 0x79, 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70, 0x6f, 0x6e, 0x6d, 0x6b, 0x6a, 0x69, 0x68, 0x66, 0x65, 0x64, 0x62, 0x61, 0x5f, 0x5e, 0x5d, 0x5b, 0x5a, 0x58, 0x56, 0x55, 0x53, 0x52, 0x50, 0x4e, 0x4d, 0x4b, 0x49, 0x47, 0x46, 0x44, 0x42, 0x40, 0x3f, 0x3d, 0x3b, 0x39, 0x37, 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x28, 0x26, 0x23, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x0f, 0x0d, 0x0b, 0x09, 0x07, 0x04, 0x02, 0x00, 0xfe, 0xfc, 0xfa, 0xf8, 0xf6, 0xf3, 0xf1, 0xef, 0xed, 0xeb, 0xe9, 0xe7, 0xe5, 0xe3, 0xe1, 0xde, 0xdc, 0xda, 0xd8, 0xd6, 0xd4, 0xd2, 0xd0, 0xce, 0xcc, 0xca, 0xc8, 0xc6, 0xc4, 0xc2, 0xc0, 0xbf, 0xbd, 0xbb, 0xb9, 0xb7, 0xb5, 0xb4, 0xb2, 0xb0, 0xae, 0xad, 0xab, 0xa9, 0xa8, 0xa6, 0xa5, 0xa3, 0xa2, 0xa0, 0x9f, 0x9d, 0x9c, 0x9a, 0x99, 0x98, 0x96, 0x95, 0x94, 0x93, 0x91, 0x90, 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85, 0x85, 0x84, 0x83, 0x82, 0x82, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x82, 0x82, 0x83, 0x84, 0x84, 0x85, 0x86, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, +0x8f, 0x90, 0x91, 0x92, 0x94, 0x95, 0x96, 0x97, 0x98, 0x9a, 0x9b, 0x9c, 0x9d, 0x9f, 0xa0, 0xa2, 0xa3, 0xa4, 0xa6, 0xa7, 0xa9, 0xaa, 0xac, 0xae, 0xaf, 0xb1, 0xb2, 0xb4, 0xb6, 0xb7, 0xb9, 0xbb, 0xbd, 0xbe, 0xc0, 0xc2, 0xc4, 0xc5, 0xc7, 0xc9, 0xcb, 0xcd, 0xcf, 0xd1, 0xd3, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xff, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f, 0x21, 0x23, 0x25, 0x27, 0x29, 0x2b, 0x2d, 0x2f, 0x31, 0x33, 0x35, 0x37, 0x39, 0x3b, 0x3d, 0x3e, 0x40, 0x42, 0x44, 0x46, 0x47, 0x49, 0x4b, 0x4c, 0x4e, 0x50, 0x51, 0x53, 0x55, 0x56, 0x58, 0x59, 0x5b, 0x5c, 0x5e, 0x5f, 0x60, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x69, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x76, 0x77, 0x78, 0x79, 0x79, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7e, 0x7e, 0x7e, 0x7d, 0x7d, 0x7d, 0x7c, 0x7c, 0x7b, 0x7b, 0x7a, 0x79, 0x79, 0x78, 0x77, 0x77, 0x76, 0x75, 0x74, 0x73, 0x73, 0x72, 0x71, 0x70, 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x67, 0x66, 0x65, 0x64, 0x62, 0x61, 0x60, 0x5f, 0x5d, 0x5c, 0x5a, 0x59, 0x58, 0x56, 0x55, 0x53, 0x52, 0x50, 0x4f, 0x4d, 0x4b, 0x4a, 0x48, 0x47, 0x45, 0x43, 0x42, 0x40, 0x3e, 0x3c, 0x3b, 0x39, 0x37, 0x35, 0x33, 0x32, 0x30, 0x2e, 0x2c, 0x2a, 0x28, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, +0x11, 0x0f, 0x0d, 0x0b, 0x09, 0x07, 0x05, 0x03, 0x01, 0xff, 0xfd, 0xfb, 0xf9, 0xf7, 0xf5, 0xf3, 0xf1, 0xef, 0xed, 0xeb, 0xe9, 0xe7, 0xe5, 0xe3, 0xe1, 0xdf, 0xdd, 0xdb, 0xda, 0xd8, 0xd6, 0xd4, 0xd2, 0xd0, 0xce, 0xcc, 0xca, 0xc9, 0xc7, 0xc5, 0xc3, 0xc1, 0xc0, 0xbe, 0xbc, 0xbb, 0xb9, 0xb7, 0xb6, 0xb4, 0xb2, 0xb1, 0xaf, 0xae, 0xac, 0xab, 0xa9, 0xa8, 0xa6, 0xa5, 0xa3, 0xa2, 0xa1, 0x9f, 0x9e, 0x9d, 0x9c, 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, 0x8d, 0x8d, 0x8c, 0x8b, 0x8a, 0x8a, 0x89, 0x88, 0x88, 0x87, 0x87, 0x86, 0x86, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x87, 0x87, 0x88, 0x88, 0x89, 0x8a, 0x8a, 0x8b, 0x8c, 0x8d, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x93, 0x94, 0x95, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0xa0, 0xa1, 0xa2, 0xa4, 0xa5, 0xa6, 0xa8, 0xa9, 0xaa, 0xac, 0xad, 0xaf, 0xb0, 0xb2, 0xb3, 0xb5, 0xb6, 0xb8, 0xb9, 0xbb, 0xbd, 0xbe, 0xc0, 0xc2, 0xc3, 0xc5, 0xc7, 0xc8, 0xca, 0xcc, 0xce, 0xcf, 0xd1, 0xd3, 0xd5, 0xd7, 0xd8, 0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4, 0xe5, 0xe7, 0xe9, 0xeb, 0xed, 0xef, 0xf1, 0xf3, 0xf5, 0xf7, 0xf9, 0xfb, 0xfc, 0xfe, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1b, 0x1d, 0x1f, 0x21, 0x23, 0x25, 0x27, 0x29, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x33, 0x35, 0x37, 0x39, 0x3a, 0x3c, 0x3e, 0x40, 0x41, 0x43, 0x44, 0x46, 0x48, 0x49, 0x4b, 0x4c, 0x4e, 0x4f, 0x51, 0x52, 0x54, +0x55, 0x56, 0x58, 0x59, 0x5a, 0x5c, 0x5d, 0x5e, 0x5f, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6e, 0x6f, 0x70, 0x71, 0x71, 0x72, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x78, 0x78, 0x78, 0x78, 0x78, 0x77, 0x77, 0x77, 0x76, 0x76, 0x76, 0x75, 0x75, 0x74, 0x74, 0x73, 0x72, 0x72, 0x71, 0x71, 0x70, 0x6f, 0x6e, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e, 0x5d, 0x5b, 0x5a, 0x59, 0x58, 0x56, 0x55, 0x54, 0x52, 0x51, 0x50, 0x4e, 0x4d, 0x4b, 0x4a, 0x48, 0x47, 0x45, 0x44, 0x42, 0x41, 0x3f, 0x3e, 0x3c, 0x3a, 0x39, 0x37, 0x35, 0x34, 0x32, 0x30, 0x2f, 0x2d, 0x2b, 0x29, 0x28, 0x26, 0x24, 0x22, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x18, 0x16, 0x14, 0x12, 0x10, 0x0e, 0x0d, 0x0b, 0x09, 0x07, 0x05, 0x03, 0x01, 0xff, 0xfe, 0xfc, 0xfa, 0xf8, 0xf6, 0xf4, 0xf2, 0xf1, 0xef, 0xed, 0xeb, 0xe9, 0xe7, 0xe5, 0xe4, 0xe2, 0xe0, 0xde, 0xdc, 0xdb, 0xd9, 0xd7, 0xd5, 0xd4, 0xd2, 0xd0, 0xce, 0xcd, 0xcb, 0xc9, 0xc8, 0xc6, 0xc4, 0xc3, 0xc1, 0xc0, 0xbe, 0xbd, 0xbb, 0xba, 0xb8, 0xb7, 0xb5, 0xb4, 0xb2, 0xb1, 0xb0, 0xae, 0xad, 0xac, 0xaa, 0xa9, 0xa8, 0xa7, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1, 0xa0, 0x9f, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x97, 0x96, 0x95, 0x94, 0x94, 0x93, 0x92, 0x92, 0x91, 0x91, 0x90, 0x90, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, +0x8c, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x91, 0x92, 0x92, 0x93, 0x93, 0x94, 0x95, 0x95, 0x96, 0x97, 0x98, 0x99, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xaf, 0xb0, 0xb1, 0xb3, 0xb4, 0xb5, 0xb7, 0xb8, 0xba, 0xbb, 0xbc, 0xbe, 0xbf, 0xc1, 0xc2, 0xc4, 0xc5, 0xc7, 0xc8, 0xca, 0xcc, 0xcd, 0xcf, 0xd0, 0xd2, 0xd4, 0xd5, 0xd7, 0xd9, 0xda, 0xdc, 0xde, 0xdf, 0xe1, 0xe3, 0xe4, 0xe6, 0xe8, 0xea, 0xeb, 0xed, 0xef, 0xf1, 0xf2, 0xf4, 0xf6, 0xf8, 0xf9, 0xfb, 0xfd, 0xff, 0x01, 0x02, 0x04, 0x06, 0x08, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x12, 0x14, 0x16, 0x18, 0x19, 0x1b, 0x1d, 0x1e, 0x20, 0x22, 0x24, 0x25, 0x27, 0x29, 0x2a, 0x2c, 0x2d, 0x2f, 0x31, 0x32, 0x34, 0x35, 0x37, 0x39, 0x3a, 0x3c, 0x3d, 0x3f, 0x40, 0x41, 0x43, 0x44, 0x46, 0x47, 0x48, 0x4a, 0x4b, 0x4c, 0x4d, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x60, 0x61, 0x62, 0x63, 0x64, 0x64, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6c, 0x6c, 0x6c, 0x6c, 0x6b, 0x6b, 0x6a, 0x6a, 0x6a, 0x69, 0x69, 0x68, 0x68, 0x67, 0x66, 0x66, 0x65, 0x65, 0x64, 0x63, 0x62, 0x62, 0x61, 0x60, 0x5f, 0x5e, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58, 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x50, 0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x49, 0x48, 0x47, 0x45, 0x44, 0x43, 0x42, 0x40, 0x3f, 0x3d, +0x3c, 0x3b, 0x39, 0x38, 0x36, 0x35, 0x33, 0x32, 0x30, 0x2f, 0x2d, 0x2c, 0x2a, 0x29, 0x27, 0x26, 0x24, 0x23, 0x21, 0x1f, 0x1e, 0x1c, 0x1b, 0x19, 0x17, 0x16, 0x14, 0x12, 0x11, 0x0f, 0x0d, 0x0c, 0x0a, 0x08, 0x07, 0x05, 0x03, 0x02, 0x00, 0xfe, 0xfd, 0xfb, 0xf9, 0xf8, 0xf6, 0xf4, 0xf3, 0xf1, 0xef, 0xee, 0xec, 0xeb, 0xe9, 0xe7, 0xe6, 0xe4, 0xe2, 0xe1, 0xdf, 0xde, 0xdc, 0xdb, 0xd9, 0xd7, 0xd6, 0xd4, 0xd3, 0xd1, 0xd0, 0xce, 0xcd, 0xcc, 0xca, 0xc9, 0xc7, 0xc6, 0xc5, 0xc3, 0xc2, 0xc0, 0xbf, 0xbe, 0xbd, 0xbb, 0xba, 0xb9, 0xb8, 0xb7, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0, 0xaf, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9, 0xa8, 0xa7, 0xa7, 0xa6, 0xa5, 0xa4, 0xa4, 0xa3, 0xa2, 0xa1, 0xa1, 0xa0, 0xa0, 0x9f, 0x9f, 0x9e, 0x9e, 0x9d, 0x9d, 0x9c, 0x9c, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9e, 0x9e, 0x9f, 0x9f, 0xa0, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, 0xa4, 0xa5, 0xa6, 0xa7, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc4, 0xc5, 0xc6, 0xc8, 0xc9, 0xca, 0xcc, 0xcd, 0xce, 0xd0, 0xd1, 0xd2, 0xd4, 0xd5, 0xd7, 0xd8, 0xd9, 0xdb, 0xdc, 0xde, 0xdf, 0xe1, 0xe2, 0xe4, 0xe5, 0xe7, 0xe8, 0xea, 0xeb, 0xed, 0xee, 0xf0, 0xf1, 0xf3, 0xf4, 0xf6, 0xf8, 0xf9, 0xfb, 0xfc, 0xfe, 0xff, 0x01, 0x02, 0x04, 0x05, 0x07, 0x09, 0x0a, 0x0c, 0x0d, 0x0f, 0x10, 0x12, 0x13, 0x15, 0x16, 0x18, 0x19, +0x1b, 0x1c, 0x1e, 0x1f, 0x21, 0x22, 0x23, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2c, 0x2d, 0x2e, 0x30, 0x31, 0x32, 0x34, 0x35, 0x36, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x4f, 0x50, 0x51, 0x52, 0x52, 0x53, 0x54, 0x54, 0x55, 0x56, 0x56, 0x57, 0x57, 0x58, 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5b, 0x5b, 0x5b, 0x5a, 0x5a, 0x5a, 0x59, 0x59, 0x58, 0x58, 0x57, 0x57, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53, 0x52, 0x51, 0x51, 0x50, 0x4f, 0x4e, 0x4d, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x44, 0x43, 0x41, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x35, 0x34, 0x33, 0x32, 0x31, 0x2f, 0x2e, 0x2d, 0x2c, 0x2a, 0x29, 0x28, 0x27, 0x25, 0x24, 0x23, 0x21, 0x20, 0x1f, 0x1d, 0x1c, 0x1b, 0x19, 0x18, 0x17, 0x15, 0x14, 0x12, 0x11, 0x10, 0x0e, 0x0d, 0x0b, 0x0a, 0x09, 0x07, 0x06, 0x04, 0x03, 0x02, 0x00, 0xff, 0xfd, 0xfc, 0xfb, 0xf9, 0xf8, 0xf6, 0xf5, 0xf4, 0xf2, 0xf1, 0xef, 0xee, 0xed, 0xeb, 0xea, 0xe9, 0xe7, 0xe6, 0xe5, 0xe3, 0xe2, 0xe1, 0xdf, 0xde, 0xdd, 0xdc, 0xda, 0xd9, 0xd8, 0xd7, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xd0, 0xce, 0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc8, 0xc7, 0xc6, 0xc5, 0xc4, 0xc3, 0xc2, 0xc1, 0xc0, 0xbf, 0xbe, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb9, 0xb8, 0xb7, 0xb7, 0xb6, 0xb5, 0xb5, 0xb4, 0xb3, 0xb3, 0xb2, 0xb2, 0xb1, +0xb1, 0xb0, 0xb0, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xad, 0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb2, 0xb2, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb6, 0xb7, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xbb, 0xbc, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xee, 0xef, 0xf0, 0xf1, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfe, 0xff, 0x00, 0x01, 0x03, 0x04, 0x05, 0x06, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x35, 0x36, 0x37, 0x38, 0x39, 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3d, 0x3e, 0x3f, 0x3f, 0x40, 0x40, 0x41, 0x42, 0x42, 0x43, 0x43, 0x43, 0x44, 0x44, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x47, 0x47, 0x47, 0x47, 0x46, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, 0x43, 0x43, 0x42, 0x42, 0x41, 0x41, 0x40, 0x40, +0x3f, 0x3f, 0x3e, 0x3e, 0x3d, 0x3c, 0x3c, 0x3b, 0x3a, 0x3a, 0x39, 0x38, 0x38, 0x37, 0x36, 0x35, 0x35, 0x34, 0x33, 0x32, 0x31, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, 0xe7, 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0, 0xdf, 0xdf, 0xde, 0xdd, 0xdc, 0xdb, 0xda, 0xda, 0xd9, 0xd8, 0xd7, 0xd7, 0xd6, 0xd5, 0xd5, 0xd4, 0xd3, 0xd3, 0xd2, 0xd1, 0xd1, 0xd0, 0xcf, 0xcf, 0xce, 0xce, 0xcd, 0xcd, 0xcc, 0xcc, 0xcb, 0xcb, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc7, 0xc7, 0xc7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcc, 0xcc, 0xcd, 0xcd, 0xce, 0xce, 0xcf, 0xcf, 0xd0, 0xd0, 0xd1, 0xd1, 0xd2, 0xd3, 0xd3, 0xd4, 0xd4, 0xd5, 0xd6, 0xd6, 0xd7, 0xd8, 0xd8, 0xd9, 0xda, 0xda, 0xdb, 0xdc, 0xdc, 0xdd, 0xde, 0xdf, 0xdf, 0xe0, 0xe1, 0xe2, 0xe2, 0xe3, 0xe4, 0xe5, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf2, +0xf3, 0xf4, 0xf5, 0xf6, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x04, 0x05, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0b, 0x0c, 0x0d, 0x0e, 0x0e, 0x0f, 0x10, 0x11, 0x11, 0x12, 0x13, 0x13, 0x14, 0x15, 0x15, 0x16, 0x17, 0x17, 0x18, 0x19, 0x19, 0x1a, 0x1b, 0x1b, 0x1c, 0x1c, 0x1d, 0x1d, 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x29, 0x29, 0x29, 0x29, 0x28, 0x28, 0x28, 0x28, 0x27, 0x27, 0x27, 0x26, 0x26, 0x26, 0x25, 0x25, 0x25, 0x24, 0x24, 0x24, 0x23, 0x23, 0x22, 0x22, 0x22, 0x21, 0x21, 0x20, 0x20, 0x20, 0x1f, 0x1f, 0x1e, 0x1e, 0x1d, 0x1d, 0x1c, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x14, 0x14, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0e, 0x0e, 0x0d, 0x0d, 0x0c, 0x0c, 0x0b, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x05, 0x05, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02, 0x01, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xee, +0xee, 0xee, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/democode/part_c64.cpp b/democode/part_c64.cpp new file mode 100644 index 0000000..d9735c8 --- /dev/null +++ b/democode/part_c64.cpp @@ -0,0 +1,1348 @@ +#include "demoshared.h" +#include "cbm64.h" +#include "part_c64.h" + +//#define DEBUG_SYNC + +#ifdef __GNUC__ +#pragma GCC optimize ("Os") +#endif + +// WARNING: Oh dang we're out of RAM. Need 2x 1000b for the 40x25 screenbuffer (chars + paths). +// All in all this is not enough with music and everything. So we use the 4x 256b scratch area as path buffer. +// -> The drawpetscii() function in this file requires 4 scratch blocks, so can't use palette data, sin-table, decomp-to-LCD, etc. + +//#define VERYFAST + +#ifdef VERYFAST +#define WAIT(x) +#else +#define WAIT(x) fglcd::delay_ms(x) +#endif + +#define PATH_BUFFER scratch0 + +#define UNPACK_EXO(dst, src) decompressRAM(&dst[0], fglcd_get_farptr(src), sizeof(src)); + +// 30, 12, 8 +enum +{ +#ifdef VERYFAST + DELAY1 = 1, + DELAY2 = 1, + DELAY3 = 1, +#else + DELAY1 = 30, + DELAY2 = 9, + DELAY3 = 8, +#endif + DELAY_CUBE = 8, + DELAY_CROSS = 4 +}; + +static const uint16_t c64_pal_PF[16] PROGMEM_FAR = +{ +0,25356,35921,44405,65535,39528,52206,27298,41799,52912,40723,23883,28152,35833,21043,41652 +}; + +static const u8 c64_bg_blue = 231; +static const Color c64bg = LCD::gencolor_inl(66,66,c64_bg_blue); +static const Color c64fg = LCD::gencolor_inl(165,165,255); + + +const unsigned C64_W = 320, C64_H = 200; +const unsigned cx0 = LCD::WIDTH/2 - C64_W/2; +const unsigned cy0 = LCD::HEIGHT/2 - C64_H/2; + +static void SYNCPOINT() +{ +#ifdef DEBUG_SYNC + LCD::fillrect(AREA_X_BEGIN, AREA_Y_BEGIN, 10, 10, LCD::gencolor_inl(0xff, 0, 0xff)); +#endif + WaitPartDone(); +#ifdef DEBUG_SYNC + LCD::fillrect(AREA_X_BEGIN, AREA_Y_BEGIN, 10, 10, 0); +#endif +} + +static NOINLINE void fillchars(const CBM64Font& charset, char c, Color fg, Color bg) +{ + u8 charbuf[40]; + fglcd::RAM::Memset(charbuf, c, 40); + for(u8 y = 0; y < 25; ++y) + charset.drawbuf(charbuf, sizeof(charbuf), cx0, cy0+8*y, fg, bg); +} + +static NOINLINE void drawborder(Color fg) +{ + Draw::fillrectfromto(AREA_X_BEGIN, AREA_Y_BEGIN, AREA_X_END, cy0-1, fg); // top + Draw::fillrectfromto(AREA_X_BEGIN, cy0, cx0-1, cy0+C64_H-1, fg); // left + Draw::fillrectfromto(cx0+C64_W, cy0, AREA_X_END, cy0+C64_H-1, fg); // right + Draw::fillrectfromto(AREA_X_BEGIN, cy0+C64_H, AREA_X_END, AREA_Y_END, fg); // bottom +} + +static void drawmainarea(Color col) +{ + LCD::fillrect(cx0, cy0, C64_W, C64_H, col); +} + +static LCD::PixelPos screenpos(u8 sx, u8 sy) +{ + return LCD::PixelPos(cx0 + sx*u16(8), cy0 + sy*u16(8)); +} + + +static FORCEINLINE void drawchar(const CBM64Font& charset, const u8 chr, u8 xchr, u8 ychr, Color fg, Color bg) +{ + LCD::PixelPos pp = screenpos(xchr, ychr); + LCD::setxywh(pp.x, pp.y, 8, 8); + charset.drawchar(chr, fg, bg); +} + +struct Cursor +{ +private: + u8 sx, sy; + u8 * const _screen; + const CBM64Font& _charset; + +public: + volatile u8 fill, done; // toggled every 333 ms + volatile u8 ign; + Color fg, bg; + + Cursor(u8 *screen, const CBM64Font& cs, u8 x, u8 y, Color fg_, Color bg_) + : sx(x), sy(y), _screen(screen), _charset(cs), fill(0), done(0), ign(0), fg(fg_), bg(bg_) + { + ev_cursorblink(this); + } + + ~Cursor() + { + // mark for exit and wait until event acknowledged + done = 1; + while(done == 1) {} + } + + FORCEINLINE LCD::PixelPos screenpos() + { + return ::screenpos(sx, sy); + } + + FORCEINLINE void move(s8 x, s8 y) + { + sx += x; + sy += y; + } + + void draw() + { + const LCD::PixelPos pp = screenpos(); + LCD::fillrect(pp.x, pp.y, 8, 8, fill ? fg : bg); + } + void off() + { + fill = 0; + ign = 1; + draw(); + } + void enter(u8 c) + { + const LCD::PixelPos pp = screenpos(); + + _screen[sx + sy*40] = c; + if(c == '\n') + LCD::fillrect(pp.x, pp.y, 8, 8, bg); + else + drawchar(_charset, c, sx, sy, fg, bg); + + u8 x = sx + 1; + if(c == '\n' || x >= 40) + { + x = 0; + ++sy; + } + sx = x; + draw(); + } + + static void ev_cursorblink(void *ud) + { + Cursor *cur = (Cursor*)ud; + cur->fill = !cur->fill; + + if(!cur->ign) + { + LCD::StateBackup sb; + cur->draw(); + } + + if(!cur->done) + evs::schedule(333, ev_cursorblink, ud); + else + cur->done = 2; + } +}; + + + +struct TypeIn +{ + Cursor& _cur; + const u8 *_txtp; + const u16 delay; + volatile u8 done; + + typedef int (*Callback)(const TypeIn& ti, int& c); + + Callback cb; + + static int DefaultProc(const TypeIn& ti, int& c) + { + return c ? ti.delay : (c = -1); + } + + TypeIn(Cursor& cur, const u8 *txt, u16 delay) + : _cur(cur), _txtp(txt), delay(delay), done(0), cb(DefaultProc) + { + } + + TypeIn(Cursor& cur, const u8 *txt, u16 delay, Callback cb_) + : _cur(cur), _txtp(txt), delay(delay), done(0), cb(cb_) + { + } + + void start() + { + ev(this); + } + + static void ev(void *ud) + { + TypeIn& self = *(TypeIn*)ud; + int c = fglcd::RAM::read(self._txtp); + ++self._txtp; + int res = self.cb(self, c); + if(res >= 0) + { + if(c >= 0) + self._cur.enter((u8)c); + evs::schedule(res, ev, ud); + } + else + self.done = 1; + } + + void wait() { while(!done) {} } +}; + +static const u8 ready_PF[] PROGMEM_FAR = "READY."; + +static NOINLINE void boot() +{ + partdone = false; + CBM64Font charset; + + charset.loadCharset2(); + + fillchars(charset, 0, 0xffff, 0); + fglcd::delay_ms(70); + + drawborder(c64fg); + + fillchars(charset, 127, c64fg, c64bg); + fglcd::delay_ms(60); + + drawmainarea(c64bg); +} + +static NOINLINE void loadshit() +{ + partdone = false; + CBM64Font charset; + charset.loadCharset2(); + +/* +" **** COMMODORE 64 BASIC V2 ****" +" 64k RAM SYSTEM 38911 BASIC BYTES FREE " +"" +"READY." +*/ + + { + static const u8 boot_PF[] PROGMEM_FAR = "\n" + " **** ATMEGA2560 BASIC V2 ****\n\n" + " 8K RAM SYSTEM 345000 LCD BYTES FREE\n\n" + "READY.\n"; + auto boot = farload(boot_PF); + charset.drawbuf(&boot[0], Countof(boot)-1, cx0, cy0, c64fg, c64bg); + } + + //DEBUG_RETURN_IF_DONE(); + + u8 screen[0x400]; + Cursor cur(screen, charset, 0, 6, c64fg, c64bg); + + //WaitPartDone(); + + static const u8 typein1_PF[] PROGMEM_FAR = "LOAD\"RVSN-ONLINE.HEX\",8,1"; + static const u8 loading1_PF[] PROGMEM_FAR = "SEARCHING FOR RVSN-ONLINE.HEX"; + static const u8 loading2_PF[] PROGMEM_FAR = "LOADING"; + static const u8 typein2_PF[] PROGMEM_FAR = "RUN\n"; + + { + { + auto typein1 = farload(typein1_PF); + fglcd::delay_ms(2200); + TypeIn tpi(cur, &typein1[0], 133); + tpi.start(); + tpi.wait(); + cur.off(); + } + //DEBUG_RETURN_IF_DONE(); + + cur.enter('\n'); + fglcd::delay_ms(100); + LCD::PixelPos pp = cur.screenpos(); + { + auto loading1 = farload(loading1_PF); + charset.drawbuf(&loading1[0], Countof(loading1)-1, pp.x, pp.y, c64fg, c64bg); + } + cur.move(0, 3); + fglcd::delay_ms(300); + { + auto loading2 = farload(loading2_PF); + charset.drawbuf(&loading2[0], Countof(loading2)-1, pp.x, pp.y+8, c64fg, c64bg); + } + //WaitPartDone(); + //DEBUG_RETURN_IF_DONE(); + fglcd::delay_ms(1800); + cur.ign = 0; + { + auto ready = farload(ready_PF); + charset.drawbuf(&ready[0], Countof(ready)-1, pp.x, pp.y+16, c64fg, c64bg); + } + //DEBUG_RETURN_IF_DONE(); + fglcd::delay_ms(800); + //WaitPartDone(); + + //DEBUG_RETURN_IF_DONE(); + } + + //cur.enter('\n'); + //fglcd::delay_ms(555); + //DEBUG_RETURN_IF_DONE(); + + { + //fglcd::delay_ms(1000); + //DEBUG_RETURN_IF_DONE(); + auto typein2 = farload(typein2_PF); + TypeIn tpi(cur, &typein2[0], 200); + tpi.start(); + tpi.wait(); + cur.off(); + } + //DEBUG_RETURN_IF_DONE(); + fglcd::delay_ms(300); + //DEBUG_RETURN_IF_DONE(); + // exomizer unpack blinker + { + applyPal16_PF(fglcd_get_farptr(c64_pal_PF), Countof(c64_pal_PF)); + TinyRng8 rng(23); + while(!partdone) + { + Color fg = palgetcolor_noinl(rng() & u8(Countof(c64_pal_PF)-1u)); + Color bg = palgetcolor_noinl(rng() & u8(Countof(c64_pal_PF)-1u)); +#ifdef MCU_IS_PC + PalBackup bk; + fuckpal(); +#endif + drawchar(charset, 0, 39, 24, fg, bg); + fglcd::delay_ms(60); + //DEBUG_RETURN_IF_DONE(); + } + drawchar(charset, 0x20, 39, 24, c64bg, c64bg); + } + partdone = false; + +#ifdef MCU_IS_PC + clearpal(0); +#endif +} + +enum MoveBit +{ + MV_UP = 1, + MV_DOWN = 2, + MV_LEFT = 4, + MV_RIGHT = 8, + MV_CROSS_UD = 16, + MV_CROSS_LR = 32, + + MV_UD = MV_UP | MV_DOWN, + MV_LR = MV_LEFT | MV_RIGHT, + MV_MOVE = MV_UD | MV_LR +}; + +struct Growers; + +static inline void addgrower(Growers& grw, u8 x, u8 y, u8 mask); + +struct Grower +{ + Grower() {} + Grower(u8 xx, u8 yy, u8 msk) : x(xx), y(yy), mask(msk) {} + + u8 x, y, mask, remstatus; + + FORCEINLINE void draw(const CBM64Font& charset, const u8 *screen, Color fg, Color bg, bool overdraw) const + { + u8 chr; + if((remstatus & MV_MOVE) && !(remstatus & (MV_CROSS_LR|MV_CROSS_UD)) && (mask & (MV_CROSS_LR|MV_CROSS_UD))) + { + if(!overdraw) + return; + chr = mask & MV_CROSS_LR ? 67 : 66; + } + else + chr = screen[u16(y)*40+x]; + drawchar(charset, chr, x, y, fg, bg); + } + + void expand(u8 *status, Growers& grw) + { + const u16 idx = u16(y)*40+x; + const u8 st = mask & status[idx]; // where can we expand to? + if(st) + { + status[idx] &= ~mask; // block further expansion to here, but allow jumping over + remstatus = status[idx]; + //LCD::setxywh_inl(cx0+u16(x)*8, cy0+u16(y)*8, 1, 1); + //LCD::sendPixel(LCD::gencolor(0xff, 0, 0)); + + if((st & (MV_RIGHT|MV_CROSS_LR)) && x < 39) + { + const u8 right = status[idx+1]; + if(right & MV_LEFT) + addgrower(grw, x+1, y, MV_MOVE); + if(x < 38 && (right & MV_CROSS_LR) && (status[idx+2] & MV_LEFT)) + addgrower(grw, x+1, y, MV_CROSS_LR); + } + + if((st & (MV_LEFT|MV_CROSS_LR)) && x) + { + const u8 left = status[idx-1]; + if(left & MV_RIGHT) + addgrower(grw, x-1, y, MV_MOVE); + if(x > 1 && (left & MV_CROSS_LR) && (status[idx-2] & MV_RIGHT)) + addgrower(grw, x-1, y, MV_CROSS_LR); + } + + if((st & (MV_DOWN|MV_CROSS_UD)) && y < 24) + { + const u8 down = status[idx+40]; + if(down & MV_UP) + addgrower(grw, x, y+1, MV_MOVE); + if(y < 23 && (down & MV_CROSS_UD) && (status[idx+2*40] & MV_UP)) + addgrower(grw, x, y+1, MV_CROSS_UD); + } + + if((st & (MV_UP|MV_CROSS_UD)) && y) + { + const u8 up = status[idx-40]; + if(up & MV_DOWN) + addgrower(grw, x, y-1, MV_MOVE); + if(y > 1 && (up & MV_CROSS_UD) && (status[idx-2*40] & MV_DOWN)) + addgrower(grw, x, y-1, MV_CROSS_UD); + } + } + } +}; + +struct Growers +{ + Grower g[40]; // prolly enuff + u8 n; + + void draw(const CBM64Font& charset, const u8 *screen, Color fg, Color bg, bool overdraw) const + { + for(u8 i = 0; i < n; ++i) + { + g[i].draw(charset, screen, fg, bg, overdraw); + } + } + + void expand(u8 *status) + { + u8 tail = Countof(g)-n; + memmove(&g[tail], &g[0], n*sizeof(Grower)); // move memory to end of buffer + n = 0; + for(; tail < Countof(g); ++tail) + { + g[tail].expand(status, *this); + } + } +}; + +static inline void addgrower(Growers& grw, u8 x, u8 y, u8 mask) +{ + //LCD::setxywh_inl(cx0+u16(x)*8, cy0+u16(y)*8, 1, 1); + //LCD::sendPixel(LCD::gencolor(0, 0xff, 0)); + + grw.g[grw.n++] = Grower(x, y, mask); +} + +struct TinyWait +{ + volatile bool done; + + TinyWait(u8 ms) + : done(false) + { + evs::schedule(ms, _ev, this); + } + + ~TinyWait() + { + while(!done) {} + } + + static void _ev(void *ud) + { + TinyWait *self = (TinyWait*)ud; + self->done = true; + } +}; + +static NOINLINE void dogrow_loop(u8 *paths, const CBM64Font& charset, const u8 *screen, Growers& gg, const Color *colors, u8 ncol, u8 delay, Color bg, bool overdraw) +{ + while(gg.n /*&& !partdone*/) + { + for(u8 i = 0; i < ncol; ++i) + { + TinyWait halt(delay); + gg.draw(charset, screen, colors[i], bg, overdraw); + } + gg.expand(&paths[0]); + } +} + +static NOINLINE void dogrow(const CBM64Font& charset, const u8 *screen, Growers& gg, const Color *colors, u8 ncol, u8 delay, Color bg, bool overdraw) +{ + u8 *paths = PATH_BUFFER; + UNPACK_EXO(paths, data_exo_paths); + + dogrow_loop(&paths[0], charset, screen, gg, colors, ncol, delay, bg, overdraw); +} + +static NOINLINE void drawscreen(const CBM64Font& charset, const u8 *screen, Color fg, Color bg, u8 exclude = 32) +{ + u8 c; + for(u8 y = 0; y < 25; ++y) + for(u8 x = 0; x < 40; ++x) + if((c = *screen++) != exclude) + drawchar(charset, c, x, y, fg, bg); +} + +static const Color burncolors_F[] PROGMEM_FAR = +{ + LCD::gencolor_inl(0xff, 0xdf, 0x5f), + LCD::gencolor_inl(0xff, 0xbf, 0x2f), + LCD::gencolor_inl(0xff, 0x8f, 0), + LCD::gencolor_inl(0xef, 0x3f, 0), + LCD::gencolor_inl(0x7f, 0x1f, 0), + LCD::gencolor_inl(0x3f, 0, 0), + LCD::gencolor_inl(0, 0, 0) +}; + +static const Color burncolorsFadeIn_F[] PROGMEM_FAR = +{ + LCD::gencolor_inl(0x3f, 0, 0), + LCD::gencolor_inl(0x7f, 0x1f, 0), + LCD::gencolor_inl(0xef, 0x3f, 0), + LCD::gencolor_inl(0xff, 0x8f, 0), + LCD::gencolor_inl(0xff, 0xbf, 0x2f), + LCD::gencolor_inl(0xff, 0xdf, 0x5f) +}; + +static u8 applyBurnColor(Color *colors) +{ + fastmemcpy_PF(colors, burncolors_F, sizeof(burncolors_F)); + return Countof(burncolors_F); +} + +static u8 applyBurnColorFadeIn(Color *colors) +{ + fastmemcpy_PF(colors, burncolorsFadeIn_F, sizeof(burncolorsFadeIn_F)); + return Countof(burncolorsFadeIn_F); +} + +static NOINLINE void drawpetscii() +{ + CBM64Font charset; + charset.loadCharset1(); + + u8 screen[0x400]; + +#define TOSCREEN(what) UNPACK_EXO(screen, what) +#define TOPATH(what) UNPACK_EXO(PATH_BUFFER, what) + + TOSCREEN(data_exo_hwmf); + partdone = false; + + Color colors[8]; + Growers gg; + + // Path appears initially + + colors[0] = c64fg; + gg.g[0] = Grower(26, 0, MV_MOVE); + gg.n = 1; + dogrow(charset, &screen[0], gg, &colors[0], 1, DELAY1, c64bg, true); + //DEBUG_RETURN_IF_DONE(); + WAIT(1000); + //SYNCPOINT(); + + // "fade out" border and change colors to darker + + drawscreen(charset, &screen[0], c64fg, 0, 0); + WAIT(1000); + drawborder(c64bg); + drawscreen(charset, &screen[0], c64bg, 0, 0); + WAIT(1000); + + // border goes black + + drawborder(0); + WAIT(1000); + //SYNCPOINT(); + + for(u8 i = 0; i < 7; ++i) + { + u8 c = i*36; + colors[i] = LCD::gencolor(c, c, c64_bg_blue+c); + } + colors[7] = 0xffff; + gg.g[0] = Grower(4, 0, MV_MOVE); + gg.g[1] = Grower(11, 0, MV_MOVE); + gg.n = 2; + + // paint over the lines to full white + + dogrow(charset, &screen[0], gg, &colors[0], Countof(colors), DELAY2, 0, false); + //DEBUG_RETURN_IF_DONE(); + //SYNCPOINT(); + + // Temporarily use the screen memory for the overlay... + TOSCREEN(data_exo_hwmf_nameoverlay); + drawscreen(charset, &screen[0], LCD::gencolor_inl(0xff, 0x30, 0), 0); + TOSCREEN(data_exo_hwmf); // ... and then put the original back + + //DEBUG_RETURN_IF_DONE(); + //WAIT(2500); + //SYNCPOINT(); + + + gg.g[0] = Grower(36, 23, MV_MOVE); + gg.n = 1; + + // last time is special... + TOPATH(data_exo_paths); + u8 *p = &PATH_BUFFER[u16(12)*40+18]; + // keep the rhombus in the center unaffected... + p[0] = 0; + p[1] = 0; + p[40] = 0; + p[41] = 0; + + // erase all the things except the center rhombus + + SYNCPOINT(); + + dogrow_loop(&PATH_BUFFER[0], charset, &screen[0], gg, &colors[0], applyBurnColor(colors), DELAY3, 0, false); + + //DEBUG_RETURN_IF_DONE(); + //WAIT(1000); + + // ------------------------------------------------------------- + SYNCPOINT(); + // ------------------------------------------------------------- + // Transition to cube starts here... + + TOSCREEN(data_exo_hwmf_cube); + TOPATH(data_exo_hwmf_path_cube); + gg.g[0] = Grower(17, 14, MV_MOVE); + gg.n = 1; + applyBurnColorFadeIn(colors); + dogrow_loop(&PATH_BUFFER[0], charset, &screen[0], gg, colors, applyBurnColorFadeIn(colors), DELAY_CUBE, 0, false); + + WAIT(1000); + + TOSCREEN(data_exo_hwmf_cross); + TOPATH(data_exo_hwmf_path_cross); + gg.g[0] = Grower(17, 13, MV_MOVE); + gg.g[1] = Grower(19, 14, MV_MOVE); + gg.g[2] = Grower(20, 12, MV_MOVE); + gg.g[3] = Grower(18, 11, MV_MOVE); + gg.n = 4; + applyBurnColorFadeIn(colors); + dogrow_loop(&PATH_BUFFER[0], charset, &screen[0], gg, colors, applyBurnColorFadeIn(colors), DELAY_CROSS, 0, false); + + //fglcd::delay_ms(333); + + // ok this works without clearing properly. and this looks cooler and more sudden imo. + + /* + TOPATH(hwmf_path_rmcc); + gg.g[0] = Grower(19, 0, MV_MOVE); + gg.g[1] = Grower(39, 13, MV_MOVE); + gg.g[2] = Grower(18, 23, MV_MOVE); + gg.g[3] = Grower(0, 12, MV_MOVE); + gg.n = 4; + applyBurnColorFadeIn(colors); + dogrow_loop(&PATH_BUFFER[0], charset, &screen[0], gg, colors, applyBurnColor(colors), DELAY_CROSS, 0, false); + */ + + //SYNCPOINT(); + + WAIT(500); +} + +static NOINLINE void plastic() +{ + DrawImageHelper img; + img.applypal(); + img.draw(0, 0); +} + +static int _specsCallback(const TypeIn& tpi, int& c) +{ + switch(c) + { + default: + return tpi.delay; + case '\n': + return 600; + case ':': + return 400; + case 0xff: + c = -1; + return -1; + } +} + +static NOINLINE void specs() +{ + partdone = false; + drawmainarea(c64bg); + + CBM64Font charset; + charset.loadCharset1(); + + u8 screen[0x400]; + TOSCREEN(data_exo_a2560_title); + //memset(screen, 128, sizeof(screen)); + TOPATH(data_exo_a2560_path); + //drawscreen(charset, screen, 0, c64bg); + + Color colors[1]; + Growers gg; + + colors[0] = 0; + gg.g[0] = Grower(39, 8, MV_LEFT); // 32, 7 + gg.n = 1; + dogrow_loop(&PATH_BUFFER[0], charset, &screen[0], gg, &colors[0], 1, 18, c64bg, true); + + charset.loadCharset2(); + //DEBUG_RETURN_IF_DONE(); + WAIT(300); + + //for(unsigned i = 0; i < 256; ++i) + // drawchar(charset, i, 24+ i % 16, i / 16, 0xffff, 0); + + + //0,25356,35921,44405,65535,39528,52206,27298,41799,52912,40723,23883,28152,35833,21043,41652 + Cursor cur(screen, charset, 0, 0, 65535, c64bg); + + // Abuse the path buffer for the type-in text as it's not needed right now + TOPATH(data_exo_specs); + TypeIn tpi(cur, &PATH_BUFFER[0], 18, _specsCallback); + //WaitPartDone(); + tpi.start(); + tpi.wait(); +} + +#define BAR_HEIGHT 65 + +static void accumulateAdd(u8vec4& a, const u8vec4& v) +{ + a.x = saturateAdd(a.x, v.x); + a.y = saturateAdd(a.y, v.y); + a.z = saturateAdd(a.z, v.z); + a.w |= v.w; +} + +static void addHighlight(u8vec4& a, u8 x) +{ + a.x = saturateAdd(a.x, x); + a.y = saturateAdd(a.y, x); + a.z = saturateAdd(a.z, x); +} + +static u8vec4 barcolscale(const u8vec4& v, u8 scale) +{ + return u8vec4( + hiscale8(v.x, scale), + hiscale8(v.y, scale), + hiscale8(v.z, scale), + v.w + ); +} + +struct NoiseState +{ + u8 intensity; +}; + +// TODO MUSICSYNC: change bar width/color on beat (3 channels) +static NOINLINE void noise() +{ + partdone = false; + loadIsinToScratch2(); + loadUsinToScratch3(); + //applyPal16_PF(fglcd_get_farptr(noisecol), Countof(noisecol)); + + //u8 rowflags[LCD::HEIGHT]; // 0: noise only, 1: has bar + + u8 intensity[BAR_HEIGHT]; + { + constexpr fp1616 step = 128.0f / BAR_HEIGHT; + fp1616 a = step; + for(u8 i = 0; i < BAR_HEIGHT-1; ++i, a += step) + { + fp1616 m = FSinCosScratch::sin((u8)a.intpart()); + intensity[i] = u8((m * 255).intpart()); + } + intensity[BAR_HEIGHT-1] = 0; // terminator + } + + LCD::set_scroll_pos(AREA_X_BEGIN); + + constexpr unsigned areaw = + LCD::CONST_scroll_area_sides(AREA_X_BEGIN, LCD::XMAX-AREA_X_END); + LCD:: set_scroll_area_sides(AREA_X_BEGIN, LCD::XMAX-AREA_X_END); + static_assert(areaw == AREA_W, "areawwtf"); + + constexpr static const fp1616 barm_PF[] PROGMEM_FAR = { 0.83f*0.7f, 1.17f*0.7f, 0.61f*0.7f }; + constexpr static const unsigned bary_PF[] PROGMEM_FAR = { 2*(LCD::WIDTH/5), 3*(LCD::WIDTH/5), 4*(LCD::WIDTH/5) }; + constexpr static const u8 barcol_PF[] PROGMEM_FAR = { + 0xaf, 0, 0, 1, + 0, 0x96, 0, 2, + 0x17, 0x30, 0xdf, 4 + }; + + auto barm = farload(barm_PF); + auto bary = farload(bary_PF); + auto barcol = (u8vec4*)(farload(barcol_PF).ptr()); + + const u16 INTERLACE = 8; + const u16 INTERLACESTEP = 3; + u16 yinterlace = AREA_Y_BEGIN; + //u16 v = 0; + //u16 vv = 0; + u16 a = 0; + + u8vec4 linebuf[AREA_H]; + TinyRng8 rng(0x37), rng2(0x91), rng3(0x63); // faster than one 16 bit rng + + interp::Interpolator<3> inp(16); + MusicSyncVar beatsync[] = + { + MusicSyncVar (inp, BEATSYNC_CHANNEL1, 0, 55, 3333), + MusicSyncVar (inp, BEATSYNC_CHANNEL2, 0, 70, 3333), + MusicSyncVar (inp, BEATSYNC_CHANNEL3, 0, 40, 3333) + }; + + while(!partdone) + { + fglcd::RAM::Memset(&linebuf[0], 0, sizeof(linebuf)); + + for(u8 i = 0; i < Countof(bary); ++i) + { + fp1616 s = FSinCosScratch::sin((u8)(barm[i] * a).intpart()); + int y = (AREA_H/2) + (s * ((AREA_H/2)-(BAR_HEIGHT/2))).intpart(); + bary[i] = y; + + // accumulateAdd colors + const u8vec4 bc = barcol[i]; + const unsigned mi = vmax(0, y - BAR_HEIGHT/2); + const unsigned ma = vmin(AREA_H, y + BAR_HEIGHT/2); + const u8 d = ma - mi; + const u8 beat = beatsync[i]; + for(u8 k = 0; k < d; ++k) + { + u8vec4& v = linebuf[mi+k]; + accumulateAdd(v, barcolscale(bc, intensity[k])); + if(beat) + addHighlight(v, beat); + } + } + + /*for(u8 i = 0; i < Countof(bary); ++i) + { + unsigned y = AREA_Y_BEGIN + (AREA_H/2) + bary[i]; + Draw::fillrectfromto(AREA_X_BEGIN, y, AREA_X_END, y, 0xffff); + }*/ + + + // DRAW LINES NOW + + const u8 xxr = rng3(); + + yinterlace = (yinterlace + INTERLACESTEP) % INTERLACE; + for(u16 YBASE = yinterlace; YBASE < AREA_H; YBASE += INTERLACE /*, ++vv*/) + { + const unsigned Y = AREA_Y_BEGIN + YBASE; + const u8vec4 linecol = linebuf[YBASE]; + //const Color basecol = LCD::gencolor_inl(linecol.x, linecol.y, linecol.z); + //Draw::fillrectfromto(AREA_X_BEGIN, Y, AREA_X_END, Y, basecol); + + LCD::setxy(AREA_X_BEGIN, Y, AREA_X_END, Y); + + const Color col1 = LCD::gencolor_inl(linecol.x, linecol.y, linecol.z); + if(linecol.w) + { + LCD::setColor(col1); + LCD::fastfill_u16(AREA_W); + rng(); // this prevents artifacts in the bg noise + } + else + { + const u8 s = USIN8FAST(u8(a+YBASE)*8); + const u8 PLUS = saturateAdd(s, 0x9f); + const Color col0 = LCD::gencolor_inl(PLUS, PLUS, PLUS); + + /*LCD::gencolor_inl( + saturateAdd(linecol.x, PLUS), + saturateAdd(linecol.y, PLUS), + saturateAdd(linecol.z, PLUS) + );*/ + + + //v += vv; + const u8 xr = xxr ^ rng2(); + FGLCD_DUFF8(u16, areaw, { + //v += (v << 1) + 42; + //v ^= (v >> 3) ^ (v << 3); + //LCD::sendPixel(palgetcolor_inl(v & 1)); + const u8 b = xr ^ rng(); + //v += b; + LCD::sendPixel( (b & 1) ? col1 : col0); + }); + //vv = v; + } + u16 scroll = u16(rng()) + rng(); + scroll = vmodpos(scroll); + LCD::set_scroll_pos(AREA_X_BEGIN + scroll); + } + + ++a; + } + LCD::set_scroll_area_sides(0, 0); + LCD::set_scroll_pos(0); + //LCD::set_refresh_rate(LCD::HZ_125); +} + +// -------------------------------------- PLASMA --------------------------------- + +static NOINLINE Color hsvcolor(u16 h, u8 s, u8 v) +{ + u8 r,g,b; + fast_hsv2rgb_8bit(h, s, v, &r, &g, &b); + return LCD::gencolor(r,g,b); +} + +// red gradient and solid green +static void palcolor_rg(u16 *pal) +{ + for(u8 i = 0; i < 128; ++i) + { + pal[i] = LCD::gencolor(i*2, 0,0); + pal[i+128] = LCD::gencolor(0,i < 80 ? 0 : 0xff,i); + } +} +static void palcolor_mush(u16 *pal) +{ + const u8 split = 19; + u8 i = 0, x = 0; + Color c1 = LCD::gencolor_inl(20, 50, 211); + Color c2 = LCD::gencolor_inl(20, 40, 0); + for(;;) + { + Color c = x ? c1 : c2; + for(u8 k = 0; k < split; ++k) + { + pal[i++] = c; + if(!i) + return; + } + x = 1 - x; + } +} + +static void palcolor_rainbow(u16 *pal) +{ + u8 i = 0; + do + pal[i] = hsvcolor(i * (HSV_HUE_MAX / 256), 255, 255); + while(++i); +} + +static void palcolor_fire(u16 *pal) +{ + u8 i = 0; + do + pal[i] = hsvcolor(0xff - i, 255, 255); + while(++i); +} + +// [white][ ------------------ black --------------------] +static void palcolor_mostblack(u16 *pal) +{ + const u16 d = 40; + fglcd::RAM::Memset(pal, 0, 512); + for(u8 i = 0; i < d; ++i) + pal[i] = 0xffff; +} + +// [ ------------------ white --------------------][black] +static void palcolor_mostwhite(u16 *pal) +{ + const u16 d = 40; + fglcd::RAM::Memset(pal, 0xff, 512); + for(u16 i = 256-d; i < 256; ++i) + pal[i] = 0; +} + +static void palcolor_cold(u16 *pal) +{ + u8 i = 0; + do + pal[i] = hsvcolor(127*8-(i/2), 255, i); + while(++i); +} + +static void palcolor_green(u16 *pal) +{ + u8 i = 0; + do + { + u16 x = (17 * i) >> 3; + pal[i] = hsvcolor(127*2+x, 225-(i>>2), 50+i); + } + while(++i); +} + +static void palcolor_c64(u16 *pal) +{ + auto pp = farload(c64_pal_PF); + u8 i = 0; + for(u8 j = 0; j < Countof(pp); ++j) + { + Color c = pp[j]; + for(u8 k = 0; k < 256 / Countof(pp); ++k) + pal[i++] = c; + } +} + +typedef void (*ColorFunc)(u16*); +static const ColorFunc plasmacolfunc_PF[] PROGMEM_FAR = +{ + /* 0 */ palcolor_rg, + /* 1 */ palcolor_mush, + /* 2 */ palcolor_rainbow, + /* 3 */ palcolor_fire, + /* 4 */ palcolor_mostblack, + /* 5 */ palcolor_mostwhite, + /* 6 */ palcolor_cold, + /* 7 */ palcolor_green, + /* 8 */ palcolor_c64, +}; + +// TODO: color scheme +struct PlasmaParams +{ + u8 interlace; + u8 interlacestep; + u8 speed; +}; + +static const PlasmaParams coolparams_PF[] PROGMEM_FAR = +{ + /* 0 */ { 2, 2, 2 }, // can overlay this over a bg picture? + /* 1 */ { 1, 1, 1 }, // TEST THIS + /* 2 */ { 5, 2, 2 }, // really nice + /* 3 */ { 13, 5, 2 }, + /* 4 */ { 13, 11, 2}, + /* 5 */ { 17, 7, 2 }, +}; + +struct PlasmaState +{ + PlasmaState() : rng(36568) {} // 34768 + + volatile u8 palchanged; + volatile u8 paramid; + volatile u8 funcid; + volatile s8 xinc; // -2 .. +2 + TinyRng16 rng; + u16 pal[256]; +}; + +static void modplasma(PlasmaState *ps, u8 mod) +{ + u8 p = ps->paramid, f = ps->funcid; + const u8 oldf = f; + s8 xi = ps->xinc; + u8 negxi = xi < 0; + xi = vabs(xi); + if(mod & 1) + --p; + if(mod & 2) + ++p; + if(mod & 4) + --f; + if(mod & 8) + ++f; + if(mod & 16) + xi = xi == 1 ? 2 : 1; + if(mod & 32) + negxi = !negxi; + + if(negxi) + xi = -xi; + ps->xinc = xi; + + p = vmodpos(p); + ps->paramid = p; + + if(f != oldf) + { + f = vmodpos(f); + ps->funcid = f; + ps->palchanged = 1; + } +} + +static void plasmasync(uint8_t channel, pmf_channel_info info, void *ud) +{ + PlasmaState *ps = (PlasmaState*)ud; + u8 r; + do + { + u16 rr = ps->rng(); + r = (u8)rr; + r &= ~(2 | 4 | 8); // only forward, never back + } + while(!r); // make sure *something* is changed everytime + r |= 8; // always jump the colorscheme + modplasma(ps, r); +} + +static void dbg_plasma(DebugThing& dbg, void *ud, u8 b, u8 bx) +{ + PlasmaState *ps = (PlasmaState*)ud; + if(bx) + modplasma(ps, b & bx); + dbg.set2Num(ps->paramid, ps->funcid); +} + +// TODO MUSICSYNC: change pattern/color on beat? +static NOINLINE void plasma() +{ + PlasmaState ps; + ps.funcid = 0; + ps.paramid = 0; + ps.xinc = 2; + ps.palchanged = 0; + + const auto allparams = farload(coolparams_PF); + + partdone = false; + loadIsinToScratch2(); + loadUsinToScratch3(); + + u16 colorbuf[256]; + + palcolor_green(colorbuf); + applyPal16_RAM(colorbuf, 256); + + u8 yinterlace = 0; + u16 t = 0; + u16 sp = 0; + + DEBUG_THING(dbg_plasma, &ps); + + musync::Override mu(BEATSYNC_CHANNEL1, plasmasync, &ps, musync::CallOnHit); + + while(!partdone) + { + const PlasmaParams pp = allparams[ps.paramid]; + if(ps.palchanged) + { + ps.palchanged = 0; + void *colf = fglcd::ProgmemFar::read(fglcd_get_farptr(plasmacolfunc_PF), ps.funcid); + ((ColorFunc)colf)(colorbuf); + applyPal16_RAM(colorbuf, 256); + } + + t += pp.speed; + const u16 ysep = LCD::HEIGHT / 2; //(LCD::HEIGHT / 2) + (UCOS8(t)) - 127; + + yinterlace += pp.interlacestep; + yinterlace = vmodpos(yinterlace, pp.interlace); + const u8 xi = ps.xinc; + for(u16 YBASE = yinterlace; YBASE < AREA_H; YBASE += pp.interlace, ++sp) + { + const u16 yline = AREA_Y_BEGIN + YBASE; + LCD::setxy(AREA_X_BEGIN, yline, AREA_X_END, yline); + + const u16 Y = YBASE + (UCOS8FAST(t) >> 3u); + + u8 x = t >> 2; + const u16 y = Y+t; + const u8 y1 = y >> 1u; + const u8 yy2 = y + y1; + const u8 y5 = (y / 5u); + const u8 tmpy = y1 + USIN8FAST(y5); + + FGLCD_DUFF4(u8, AREA_W/3, { + u8 v = USIN8FAST(x + t); + //const u8 vx = v; + v += tmpy; + //const u8 v2 = UCOS8(x+USIN8(vx)+y); + v += USIN8FAST(USIN8FAST(x+yy2) >> 1u); + //v += USIN8(y + UCOS8(x >> (YBASE&1))); // old red plasma: v += USIN8(y + UCOS8(x)); + v += USIN8FAST(ulo8(y) + UCOS8FAST(x)); + + LCD::sendPixel(palgetcolor_inl(v)); + LCD::sendPixel(); + LCD::sendPixel(); + x += xi; + }); + } + } +} + +static NOINLINE void guru() +{ + partdone = false; + + DrawFont font(scratch3); + + const u8 THICK = 3; + const u8 PAD = 4; + + const typename LCD::PixelPos begin(AREA_X_BEGIN+PAD+THICK + 5, AREA_Y_BEGIN+PAD+THICK + 30); + typename LCD::PixelPos pp = begin; + //font.template drawStr(pp, fglcd_get_farptr(blah), 0xffff, LCD::gencolor_inl(0,0,0x3f)); + pp = font.template drawStr(pp, fglcd_get_farptr(data_raw_guru), LCD::gencolor_inl(255,0,0), 0); + + const Color red = LCD::gencolor_inl(255,0,0); + + //DoNextPartIn(2600); + for(u8 i = 0; !partdone; ++i) + { + Draw::borderrectfromto(begin.x-PAD-THICK, begin.y-PAD-THICK, + AREA_X_END-PAD, pp.y + PAD, + (i & 4) ? 0 : red, THICK); + WAIT(125); + } + + +} + +static NOINLINE void readyready() +{ + partdone = false; + CBM64Font charset; + charset.loadCharset2(); + + u8 screen[0x400]; + u8 *p = PATH_BUFFER; + for(u8 y = 0; y < 25; ++y) + { + fastmemcpy_PF(p, ready_PF, Countof(ready_PF)-1); // skip \0 + p += Countof(ready_PF)-1; + *p++ = '\n'; + } + p[-1] = 0; + + Cursor cur(screen, charset, 0, 0, c64fg, c64bg); + cur.off(); + TypeIn tpi(cur, PATH_BUFFER, 1); + tpi.start(); + tpi.wait(); + WAIT(1022); + + TinyRng16 rng(44); + +#ifdef MCU_IS_PC + PalBackup bk; + fuckpal(); +#endif + + //DoNextPartIn(444); + while(!partdone) + { + for(u8 y = 0; y < 25 && !partdone; ++y) + { + for(u8 x = 0; x < 40; ++x) + drawchar(charset, (u8)rng(), x, y, c64fg, c64bg); + WAIT(20); + } + } +} + +static NOINLINE void fillarea(Color col) +{ + Draw::fillrectfromto(AREA_X_BEGIN, AREA_Y_BEGIN, AREA_X_END, AREA_Y_END, col); +} + +void warmup() +{ + for(u8 i = 0; i < 50; i += 4) + { + fillarea(LCD::gencolor(i,i,i)); + WAIT(20); + } +} + +demopart part_c64() +{ + plastic(); evs::killAllEvents(); + //WAIT(300); + warmup(); evs::killAllEvents(); + boot(); evs::killAllEvents(); + //WAIT(300); + loadshit(); evs::killAllEvents(); + //WAIT(300); + specs(); evs::killAllEvents(); + part_layerchess(); evs::killAllEvents(); + fillarea(0); + part_sierpscroll(); evs::killAllEvents(); + noise(); evs::killAllEvents(); + plasma(); evs::killAllEvents(); + fillarea(0); + guru(); evs::killAllEvents(); + fillarea(0); + WAIT(50); + boot(); evs::killAllEvents(); + WAIT(100); + readyready(); evs::killAllEvents(); + fillarea(0); + WAIT(50); + boot(); evs::killAllEvents(); + WAIT(200); + drawpetscii(); evs::killAllEvents(); +} diff --git a/democode/part_c64.h b/democode/part_c64.h new file mode 100644 index 0000000..1e8c0ac --- /dev/null +++ b/democode/part_c64.h @@ -0,0 +1,12 @@ +#pragma once + +// Draw area inside the plastic frame +enum PartLimits +{ + AREA_X_BEGIN = 31, + AREA_Y_BEGIN = 32, + AREA_X_END = 445, // inclusive + AREA_Y_END = 288, + AREA_W = AREA_X_END - AREA_X_BEGIN + 1, + AREA_H = AREA_Y_END - AREA_Y_BEGIN + 1, +}; diff --git a/democode/part_greet.cpp b/democode/part_greet.cpp new file mode 100644 index 0000000..46d2324 --- /dev/null +++ b/democode/part_greet.cpp @@ -0,0 +1,565 @@ +#include "demoshared.h" +#include "rotozoom.h" + + +// fake struct that decompresses to nothing +// this is required to instantiate the DrawMesh that provides the space for the triangle soup generated from data_revisionflat_obj +struct data_revisionbroken_obj_FAKE +{ +static constexpr inline fp1616 scale() { return data_revisionflat_obj::scale(); } +static const unsigned Ntris = 95; // data_revisionflat_obj::Ntris; +// ^ NOTE: DO NOT INCREASE! This is the hard limit to fit into RAM without problems. +static const unsigned Nverts = Ntris * 3; +static const unsigned paloffs = 0; +static const bool blocked = false; +static const unsigned npal = 1; +static const unsigned short pal[1] PROGMEM_FAR; +static const PackType packtype = PK_RLE; +static const unsigned windowsize = 0; +static const unsigned ndata = 1; +static const unsigned char data[1] PROGMEM_FAR; +static const unsigned packedsize = 1; +static const unsigned fullsize = + Ntris // color indices + + Nverts * sizeof(svec3); // per-tri color + per-vertex coord, no index data +static const bool Indexed = false; +}; + +const unsigned char data_revisionbroken_obj_FAKE::data[1] = { 0 }; +const unsigned short data_revisionbroken_obj_FAKE::pal[1] = { 0 }; + +struct GreetBaseState +{ + raster::Params rp; + raster::ClearHelper<4> clr; + Camera::Matrix camMat; + + NOINLINE GreetBaseState() + { + rp.align = 2; + rp.alignoffs = 0; + //rp.face = 2; + rp.incr = 2; + //rp.backfaceoffs = 1; + //rp.backfaceColorOffs = 0; + rp.rubmul = 255; + rp.glitchmul = 0; + + // camera doesn't change, so this can be done here and cached + Camera cam(8); + cam.pos(vec3(0, 0, -15)); + cam.lookAt(vec3(0)); + camMat = cam.calcMatrix(); + } +}; + +struct BrokenState : GreetBaseState +{ + DrawMesh mesh; + svec2 originalVerts2D[data_revisionbroken_obj_FAKE::Nverts]; // don't have to save Z because it's known to be 0 (saves precious space!) + + raster::TriFilterResult filt; + interp::Interpolator<2> interpolator; + interp::FancyInterpolatedValue brokenness; + interp::FancyInterpolatedValue icol; + volatile u8 done; + volatile bool mustRecalcVerts; + volatile bool isRepaired; + //u8 repairedTriIdx; + + static FORCEINLINE svec2 to2d(const svec3& v) + { + return svec2(v.x, v.y); + } + + static void ev_recalc(void *ud) + { + BrokenState *self = (BrokenState*)ud; + if(self->done) + { + self->done = 2; + return; + } + self->mustRecalcVerts = true; + if(self->brokenness) + evs::schedule(80, ev_recalc, ud); + else + { + self->isRepaired = true; + self->done = 2; + } + } + + static void ev_start_shrink(void *ud) + { + BrokenState *self = (BrokenState*)ud; + self->brokenness.interpolateTo(0, 300); + } + + ~BrokenState() + { + if(done < 2) + { + done = 1; + while(done == 1) {} + } + } + + BrokenState() + : brokenness(20000) + , icol(0) + , done(0) + , mustRecalcVerts(true) + , isRepaired(false) + //, repairedTriIdx(0) + { +#ifdef _DEBUG + printf("BrokenState total size = %u\n", unsigned(sizeof(*this))); +#endif + // save original vertex data right after decompressing + // HACK: since we don't need the triangle soup mesh data here, + // and there is abolutely no space to spare: + // just plow over the storage for now + typedef DecompMeshData Original; + static_assert(sizeof(Original) < sizeof(mesh), "won't fit"); + Original *orig = new(&mesh) Original; // this decompresses the data in place + + const raster::Tri * const tris = orig->getTriData(); + const svec3 * const vtxIn = orig->getVertexData(); + svec2 *vtxOut = &originalVerts2D[0]; + + // skip the first few verts for which there's not enough RAM anymore. + // gives the entire thing a nice, broken feel too + for(u8 i = Original::Ntris - mesh.Ntris; i < Original::Ntris; ++i) + { + raster::Tri tri = tris[i]; + *vtxOut++ = to2d(vtxIn[tri.a]); + *vtxOut++ = to2d(vtxIn[tri.b]); + *vtxOut++ = to2d(vtxIn[tri.c]); + } + FGLCD_ASSERT(vtxOut - &originalVerts2D[0] == mesh.Nverts, "brkvtx"); + + // re-init the actual storage + new(&mesh) decltype(mesh); + filt = mesh.noFilter(); + + interpolator.add(brokenness); + interpolator.add(icol); + icol.interpolateTo(0xff, 300); + brokenness.interpolateTo(60000, 600); + interpolator.start(16); + + evs::schedule(4000, ev_start_shrink, this); + ev_recalc(this); + } + + void recalcVerts() // recalc vertex positions: use brokenness to translate away by some factor + { + TinyRng16 rng(34863); + const u16 scale = brokenness; + u8 k = 0; + s16 /*tx, ty,*/ tz; //randomized + scaled by scale factor + for(u16 i = 0; i < Countof(originalVerts2D); ++i, --k) + { + // offset verts in groups of 3 + if(!k) + { + k = 3; + //tx = scale16(rng() & 0x0fff, scale); + //ty = scale16(rng() & 0x0fff, scale); + tz = scale16(rng() & 0x7fff, scale); + u8 r = (u8)rng(); + /*if(r & 1) + tx = -tx; + if(r & 2) + ty = -ty;*/ + if(r & 4) + tz = -tz; + } + svec2 v2 = originalVerts2D[i]; + svec3 v = svec3(v2.x, v2.y, fp88::raw(tz) ); + /*v.x.f.i += tx; + v.y.f.i += ty;*/ + mesh.getVertexData()[i] = v; + } + } + + bool update(u16 c) + { + if(mustRecalcVerts) + { + recalcVerts(); + mustRecalcVerts = false; + } + u8 col = icol; + palsetcolor(0, LCD::gencolor(col,col,col)); + + mat4rz mrz(c+c/4, FSinCosScratch()); + mat4ry mry(c, FSinCosScratch()); + const auto m = camMat * mry * mrz; + mesh.transform(m, ivec2(LCD::WIDTH / 2, LCD::HEIGHT / 2 - 40)); + + clr.add(mesh.getAABB(), 6); + clr.clear(0); + + mesh.draw(mesh.frontFaceIdxs(), filt.nFront, rp, 1, 0); + + return (ulo8(c) & 0x7f) == 0x3f && isRepaired; // true when it's orthogonal to camera and not visible + } +}; + +struct RvnState : GreetBaseState +{ + DrawMesh mesh; + + raster::TriFilterResult filt; + interp::Interpolator<1> interpolator; + interp::FancyInterpolatedValue icol; + + + static void ev_fadeout(void *ud) + { + RvnState *rvn = (RvnState*)ud; + rvn->icol.interpolateTo(0, 333); + rvn->interpolator.add(rvn->icol); + rvn->interpolator.start(16); + } + + RvnState() + : icol(0xff) + { + mesh.applyColorOffs(s8(0xff)); // uses only 1 color, make that the last one (sofa starts at color 0 so the first few are taken) + + filt = mesh.noFilter(); + palsetcolor(0xff, 0xffff); + + evs::schedule(7000, ev_fadeout, this); + } + + bool update(u16 c) + { + const u8 col = icol; + palsetcolor(0xff, LCD::gencolor(col,col,col)); + + mat4rz mrz(c+c/4, FSinCosScratch()); + mat4ry mry(c, FSinCosScratch()); + const auto m = camMat * mry * mrz; + mesh.transform(m, ivec2(LCD::WIDTH / 2, LCD::HEIGHT / 2 - 40)); + + clr.add(mesh.getAABB(), 6); + clr.clear(0); + mesh.draw(mesh.frontFaceIdxs(), filt.nFront, rp, 1, 0); + + return col == 0; + } +}; + +struct SofaState : GreetBaseState +{ + DrawMesh sofa; + u8 oldrotval; + interp::FancyInterpolatedValue isub; + interp::Interpolator<1> interpolator; + u8 oldsub; + u8 exiting; + + SofaState() + : oldrotval(0) + , isub(0xff) + , oldsub(0) + , exiting(false) + { + sofa.applypalNoShade(sofa.npal); // backup colors + + Camera cam(8); + cam.pos(vec3(0, 4.4f, -15)); + cam.lookAt(vec3(0)); + camMat = cam.calcMatrix(); + //rp.align = 1; + //rp.incr = 1; + + isub.interpolateTo(0, 533); + interpolator.add(isub); + interpolator.start(16); + } + + /*FORCEINLINE void forceNextRedraw() + { + oldrotval ^= 0xff; // good enough + }*/ + + void update(u16 c) + { + const u8 ci = (u8)scale16by8(c, 0x74); + const u8 rotval = ISIN8FAST(ci) >> 2; + const u8 sub = isub; + + if(sub != oldsub) + { + oldsub = sub; + dampenColors(0, sub, sofa.npal); + goto redraw; + } + + if(rotval != oldrotval) + { +redraw: + oldrotval = rotval; + mat4ry mry(127 + rotval, FSinCosScratch()); + const auto m = camMat * mry; + sofa.transform(m, ivec2(LCD::WIDTH / 2, LCD::HEIGHT / 2 + 120)); + + clr.add(sofa.getAABB(), 1); + clr.clear(0); + + raster::TriFilterResult sofafilt = sofa.filter(); + sofa.sortIndexZ(sofa.frontFaceIdxs(), sofafilt.nFront); + sofa.draw(sofa.frontFaceIdxs(), sofafilt.nFront, rp, 1, 0); + + rp.alignoffs = 1 - rp.alignoffs; + } + } + + bool finish() + { + if(!exiting) + { + exiting = true; + isub.interpolateTo(255, 1000); + } + return !isub.isInterpolating(); + } +}; + +struct CreditsThing +{ + typedef DrawFont Font; + + char text[256]; // enuff + Font font; + FontTyper typer; + u8 exiting; + volatile u8 isdone; + + static uint16_t callback(char& c, LCD::PixelPos pp, void *ud) + { + if(c == '\n') + return 400; + return c ? 50 : 0; + } + + CreditsThing() + : font(scratch3) + , typer(font, &text[0], callback, this) + , exiting(false) + , isdone(false) + { + // unpack first + decompressRAM(&text[0], fglcd_get_farptr(data_exo_credits), sizeof(data_exo_credits)); + + // font needs the scratch page that the depacker clobbers + font.rebuildIndex(); + + typer.setPos(LCD::WIDTH / 4 + 40, 30); + + typer.start(500); + } + + static void ev_setdone(void *ud) + { + CreditsThing *self = (CreditsThing*)ud; + self->isdone = 1; + } + + bool update() + { + if(!exiting) + { + typer.update(); + if(typer.done()) + { + exiting = true; + evs::schedule(3000, ev_setdone, this); + } + } + + return isdone && partdone; + } + + + FORCEINLINE uint8_t done() const + { + return typer.done(); + } +}; + +struct RotozoomThing +{ + rotozoom::Helper impl; + interp::Interpolator<2> inp; + interp::FancyInterpolatedValue fade, yclr; + u8 exiting; + u16 _y; + + RotozoomThing() + : inp(16), fade(255), yclr(0), exiting(0), _y(0) + { + impl.img.applypal(impl.img.npal); // backup palette + fade.interpolateTo(0, 900); + yclr.interpolateTo(LCD::HEIGHT / 2, 200); + inp.add(fade); + inp.add(yclr); + } + + bool update(u16 c) + { + u8 f = fade; + if(f) + dampenColors(0, f, impl.img.npal); + u16 yy = yclr; + u16 y = _y; + if(y < yy) + { + do + { + LCD::setxy(0, y, LCD::XMAX, y); + LCD::setColor(0); + LCD::fastfill_u16(LCD::WIDTH); + ++y; + } + while(y < yy); + _y = y; + } + + impl.draw(c); + + if(exiting) + { + return f == 255; + } + else if(partdone) // set by music sync + { + exiting = 1; + fade.interpolateTo(255, 2000); + } + + return false; + } +}; + + +// part1: BrokenState +// part2: SofaState + RvnState +// part3: SofaState + CreditsThing +struct GreetShared +{ + GreetShared() + : c(0) + , stateId(0) + + { + new(&part0.brk) BrokenState(); + } + + ~GreetShared() {} + + u16 c; + u8 stateId; + + union + { + struct + { + BrokenState brk; + } part0; + + struct + { + SofaState sofa; + RvnState rvn; + } part1; + + struct + { + SofaState sofa; + CreditsThing credits; + } part2; + + struct + { + RotozoomThing roto; + } part3; + }; + + void update() + { + switch(stateId) + { + case 0: + if(part0.brk.update(c++)) + next(); // and fall through + else + break; + case 1: + part1.sofa.update(c++); + if(part1.rvn.update(c)) + next(); // and fall through + else + break; + case 2: + part2.sofa.update(c++); + if(part2.credits.update() && part2.sofa.finish()) + next(); // and fall through + else + break; + case 3: + if(part3.roto.update(c += 66)) + next(); // and fall through + else + break; + } + } + + void next() + { + switch(stateId) + { + case 0: + part0.brk.~BrokenState(); + new(&part1.rvn) RvnState; + new(&part1.sofa) SofaState; + break; + case 1: + part1.rvn.~RvnState(); + new (&part2.credits) CreditsThing; + // sofa stays as it is + break; + case 2: + part2.credits.~CreditsThing(); + part2.sofa.~SofaState(); + new (&part3.roto) RotozoomThing; + partdone = false; + break; + case 3: + part3.roto.~RotozoomThing(); + } + + ++stateId; + } +}; + +demopart part_greet() +{ + loadIsinToScratch2(); + //loadUsinToScratch3(); + + GreetShared state; + DEBUG_PRINTF("sizeof(GreetShared) = %u\n", unsigned(sizeof(GreetShared))); + DEBUG_PRINTF("sizeof(SofaState) = %u\n", unsigned(sizeof(SofaState))); + + partdone = false; + do + state.update(); + while(state.stateId < 4); + partdone = false; +} diff --git a/democode/part_introstuff.cpp b/democode/part_introstuff.cpp new file mode 100644 index 0000000..e04af56 --- /dev/null +++ b/democode/part_introstuff.cpp @@ -0,0 +1,152 @@ +#include "demoshared.h" + +#ifdef __GNUC__ +#pragma GCC optimize ("Os") +#endif + +demopart part_thissideup() +{ + DrawImageHelper img; + img.applypal(); + + partdone = false; + for(u8 y = LCD::HEIGHT/3; y > 10 && !partdone; y -= 1) + { + img.draw(LCD::WIDTH/2 - img.w/2, y); + LCD::fillrect(LCD::WIDTH/2 - img.w/2, y+img.h, img.w, 1, 0); + fglcd::delay_ms(20); + if(y < 10+64) + { + u8 c = (y - 10)*3; + palsetcolor(1, LCD::gencolor(c,c,c)); + } + } + + cls(); +} + + + +typedef DrawFont TheFont; +typedef FontTyper TypeText; + +#define UNPACK_EXO(dst, src) decompressRAM(&dst[0], fglcd_get_farptr(src), sizeof(src)); + +static uint16_t cb1(char& c, LCD::PixelPos pp, void *ud) +{ + return c ? 70 : 0; +} + +demopart part_intro1() +{ + partdone = false; + TheFont font(scratch3); + auto text = farload(data_raw_intro1); + TypeText typer(font, (const char*)&text[0], cb1, NULL); + LCD::PixelPos sz = font.calcSize((const char*)&text[0]); + typer.setPos(LCD::WIDTH/2 - sz.x/2, LCD::HEIGHT/2 - sz.y/2); + typer.start(10); + + while(!typer.done()) + typer.update(); + + WaitPartDone(); +} + + +struct Intro2State +{ + TypeText typer; + uint8_t state; + LCD::PixelPos pos; + + static uint16_t cb(char& c, LCD::PixelPos pp, void *ud) + { + if(!c) + return 0; + if(c == '\n') + { + ((Intro2State*)ud)->next(); + c = 0; + return 600; + } + return 20; + } + + Intro2State(const TheFont& font, const char *text) + : typer(font, text, cb, this), state(0) + { + } + + void setPos(LCD::DimType x, LCD::DimType y) + { + typer.setPos(x, y); + pos.x = x; + pos.y = y; + } + + void next() + { + ++state; + setcolor(); + LCD::PixelPos p = pos; + p.x += 3; + p.y += 4; + setPos(p.x, p.y); + } + + void setcolor() + { + u8 c = 120 + state * 30; + u8 i = 20 + 10*state; + typer.fgcol = LCD::gencolor(c,c,c); + typer.bgcol = LCD::gencolor(i,i,i); + } +}; + +demopart part_intro2() +{ + partdone = false; + char text[512]; + UNPACK_EXO(text, data_exo_intro2); + TheFont font(scratch3); + + Intro2State state(font, text); + + LCD::PixelPos sz = font.calcSize((const char*)&text[0]); + state.setPos(LCD::WIDTH/2 - sz.x/2, LCD::HEIGHT/2 - font.lineHeight()/2); + state.typer.start(10); + while(!state.typer.done()) + state.typer.update(); + + WaitPartDone(); +} + +demopart part_end() +{ + char text[512]; + UNPACK_EXO(text, data_exo_theend); + + LCD::PixelPos pp; + { + TheFont font(scratch3); + LCD::PixelPos sz = font.calcSize((const char*)&text[0]); + pp.x = LCD::WIDTH/2 - sz.x/2; + pp.y = LCD::HEIGHT/2 - sz.y/2 - data_github_png::h/2; + pp = font.drawStr(pp, &text[0], 0xffff, 0); + } + + { + DrawImageHelper img; + img.applypal(); + img.draw(LCD::WIDTH/2 - img.w/2, pp.y + img.h); + } + + DoNextPartIn(12000); + WaitPartDone(); + music_stop(); + cls(); + + DoNextPartIn(2000); + WaitPartDone(); +} diff --git a/democode/part_landscape.cpp b/democode/part_landscape.cpp new file mode 100644 index 0000000..d30219c --- /dev/null +++ b/democode/part_landscape.cpp @@ -0,0 +1,184 @@ +#include "demoshared.h" + +#ifdef __GNUC__ +#pragma GCC optimize ("Os") +#endif + +typedef DrawMesh TheMesh; + +enum Colors +{ + CLOUD1 = LCD::gencolor_inl(0xff, 0xff, 0xff), + CLOUD2 = LCD::gencolor_inl(0xee, 0xee, 0xee), + CLOUD3 = LCD::gencolor_inl(0xdd, 0xdd, 0xdd), + CLOUD4 = LCD::gencolor_inl(0xcc, 0xcc, 0xcc), + CLOUD5 = LCD::gencolor_inl(0xbb, 0xbb, 0xbb), + CLOUD6 = LCD::gencolor_inl(0xaa, 0xaa, 0xaa), + CLOUD7 = LCD::gencolor_inl(0x99, 0x99, 0x99), + CLOUD8 = LCD::gencolor_inl(0x88, 0x88, 0x88), + SKY = LCD::gencolor_inl(0x42, 0x80, 0xf1), + WHAT = LCD::gencolor_inl(0xff, 0, 0), + LAND1 = LCD::gencolor_inl(0x66, 0xee, 0x44), + LAND2 = LCD::gencolor_inl(0x44, 0xdd, 0x33), + LAND3 = LCD::gencolor_inl(0x33, 0xbb, 0x22), + LAND4 = LCD::gencolor_inl(0x22, 0x99, 0x11), + LAND5 = LCD::gencolor_inl(0x11, 0x77, 0x00), +}; + +// heights are carefully measured by examining +enum Limits +{ + TOP_BAND_Y = 0, + TOP_BAND_H = 46, + BOTTOM_BAND_H = 52, //44 without LAND5 + BOTTOM_BAND_Y = LCD::YMAX - BOTTOM_BAND_H - 20, + + SPRITE_W = 78, + SPRITE_H = 50, +}; + + + +static FORCEINLINE u8 cloud(unsigned x) +{ + s8 w = ISIN8FAST(x/8) / 4; + s8 q = ISIN8FAST(x/2 + w) / 2; + u8 y1 = (USIN8FAST(x + q)) / 16; // usin == 127+isin + u8 y2 = (USIN8FAST(x + x/2 - q)) / 8; + + return vmax(y1, y2); +} + +static FORCEINLINE u8 land(unsigned x) +{ + const u8 y = cloud(~x); + return hiscale8(s8(y+u8(0x3f)), s8(y+0x1f)); // yeah whatever +} +static FORCEINLINE unsigned scl(unsigned x) +{ + return x >> 4; +} + +template +static FORCEINLINE void adv(u8& pos, u8 y) +{ + if(pos < y) + { + LCD::setColor(col); + LCD::fastfill_u8(y - pos); + pos = y; + } +} + +#define TOTEX + +demopart part_test_landscape() +{ + partdone = false; + LCD::clear(SKY); + + loadIsinToScratch2(); + loadUsinToScratch3(); + u8 fb[SPRITE_W * SPRITE_H]; + + TheMesh mesh; + mesh.applypal(11, 7, 0xdf, 0xdf); + palsetcolor(0xff, SKY); + + Draw::fillrectfromto(0, BOTTOM_BAND_Y + BOTTOM_BAND_H, LCD::XMAX, LCD::YMAX, LAND1); + + unsigned a = 0, b = 554, c = 3372, d = 8773, e = 2231, f = 291, g = 5532; + unsigned q = 0; + raster::Params rp; + rp.align = 1; + rp.alignoffs = 0; + //rp.face = 1; + rp.incr = 1; + //rp.backfaceoffs = 1; + rp.rubmul = 0; + rp.glitchmul = 0; + +#ifndef TOTEX + raster::ClearHelper<2> clr; +#endif + + while(!partdone) + { + LCD::set_address_mode(LCD::ADDRMODE_LR_TB); // draw columnwise + LCD::setxywh_inl(TOP_BAND_Y, 0, TOP_BAND_H, LCD::WIDTH); // freaky x/y-addrmode swap shit + + for(unsigned x = 0; x < LCD::WIDTH; x += 2) + { + u8 pos = 0; + adv(pos, cloud(scl(a)+x)); + adv(pos, 10+cloud(scl(b)+x)); + adv(pos, 15+cloud(scl(c)+x)); + adv(pos, TOP_BAND_H); + + pos = 0; + adv(pos, cloud(scl(d)+x)); + adv(pos, 10+cloud(scl(e)+x)); + adv(pos, 15+cloud(scl(f)+x)); + adv(pos, TOP_BAND_H); + } + + LCD::set_address_mode(LCD::ADDRMODE_RL_TB); // flip it so the math can stay as-is + LCD::setxywh_inl(BOTTOM_BAND_Y, 0, BOTTOM_BAND_H, LCD::WIDTH); // freaky x/y-addrmode swap shit + + for(unsigned x = 0; x < LCD::WIDTH; ++x) + { + u8 pos = 0; + adv(pos, land(scl(a)+x)); + adv(pos, cloud(scl(c)+x)); + adv(pos, 10+cloud(scl(d)+x)); + adv(pos, 22+ land(scl(f)+x)); + adv(pos, 30+ land(scl(g)+x)); + adv(pos, BOTTOM_BAND_H); + } + + a += 57; + b += 49; + c += 41; + d += 31; + e += 25; + f += 19; + g += 10; + + // ----- update ship buf ---- + LCD::set_address_mode(LCD::ADDRMODE_LANDSCAPE); + + const unsigned ix = (LCD::WIDTH / 2 - 60) + cloud(q); + const unsigned iy = (LCD::HEIGHT / 2 - 40) + cloud(q+q/2); + + fglcd::RAM::Memset(fb, 0xff, sizeof(fb)); // sky color +#ifdef TOTEX + mat4t mt(SPRITE_W / 2 + 2, SPRITE_H / 2, 0); +#else + mat4t mt(SPRITE_W / 2 + 2 + ix, SPRITE_H / 2 + iy, 0); +#endif + mat4rx mrx(q++, FSinCosScratch()); + mat4s ms(10); + const auto m = mt * ms * mrx; + mesh.transform(m); + raster::TriFilterResult filt = mesh.filter(); + uint8_t *front = mesh.frontFaceIdxs(); + mesh.sortIndexZ(front, filt.nFront); + +#ifdef TOTEX + mesh.drawToTex(mesh.frontFaceIdxs(), filt.nFront, rp, 0, 0, fb, SPRITE_W, SPRITE_H); + Draw::drawimageRaw(ix, iy, SPRITE_W, SPRITE_H, fb); +#else + clr.add(mesh.getAABB(), 4); + clr.clear(SKY); + mesh.draw(mesh.frontFaceIdxs(), filt.nFront, rp, 0, 0); + //mesh.drawZ_DEBUG(mesh.frontFaceIdxs(), filt.nFront, rp, 0, 0); + //mesh.drawWireframe(mesh.backFaceIdxs(filt.nBack), filt.nBack, 0); + //mesh.drawWireframe(mesh.frontFaceIdxs(), filt.nFront, 0xffff); + //mesh.drawWireframeZ_DEBUG(mesh.frontFaceIdxs(), filt.nFront); +#endif + + + } + + LCD::set_address_mode(LCD::ADDRMODE_LANDSCAPE); +} diff --git a/democode/part_md.cpp b/democode/part_md.cpp new file mode 100644 index 0000000..f0a649f --- /dev/null +++ b/democode/part_md.cpp @@ -0,0 +1,150 @@ +#include "demoshared.h" + + +struct StretchState +{ + fp1616 stretch, accu; +}; + +static void makestretch(StretchState& ss, u16 *stretches, unsigned n, fp1616 add) +{ + fp1616 stretch = ss.stretch; + fp1616 accu = ss.accu; + const fp1616 m = 0.5f; + for(u16 y = 0; y < n; ++y) + { + stretch += 1; + stretch -= accu * m; + stretches[y] = stretch.intpart(); + accu += add; + } + ss.stretch = stretch; + ss.accu = accu; +} + +demopart part_megadrivescroll() +{ + DecompImageBlocks img; + img.applypal(); + + loadIsinToScratch2(); + loadUsinToScratch3(); // decompression clobbers this, so do it after + + u16 stretches[LCD::HEIGHT]; + //makestretch(&stretches[0], LCD::HEIGHT, -2, 3.4f / LCD::HEIGHT); + + u8 scrollspeed[LCD::HEIGHT]; + s8 xshift[LCD::HEIGHT]; + s8 xtileoffs[LCD::HEIGHT]; + { + { + StretchState ss; + ss.accu = 1.5f; + ss.stretch = 0; + constexpr u16 what = LCD::HEIGHT / 2; + makestretch(ss, &stretches[0], LCD::HEIGHT / 2, + -2.1f / what); + makestretch(ss, &stretches[LCD::HEIGHT / 2], LCD::HEIGHT / 2, + 2.1f / what); + } + + u16 halfstretch = vmax(stretches[0], stretches[LCD::HEIGHT-1]) / 2; + constexpr fp1616 f = 127.0f / LCD::HEIGHT; // upper half wave + constexpr fp1616 depth = 0.4f; + fp1616 fa = 0; + for(unsigned i = 0; i < LCD::HEIGHT; ++i) + { + scrollspeed[i] = 20 + (u8)vabs(halfstretch - stretches[i]) / 4; + s16 x = (depth * ISIN8FAST(fa.intpart())).intpart(); + fa += f; + s8 xoffs = 0; + while(x >= img.blockw) + { + x -= img.blockw; + --xoffs; + } + while(x < 0) + { + x += img.blockw; + ++xoffs; + } + xshift[i] = (s8)x; //scrollspeed[i]; + xtileoffs[i] = xoffs; + } + + } + + partdone = 0; + u16 a = 0xff; // glitches when starting with 0 + const u8 INTERLACE = 4; + const u8 STAGGER = INTERLACE / 2; + const u16 BLOCK_HEIGHT = 32; + const u16 largeblocks = (img.w * img.h) / (img.blockw * 32); + u8 ymasteroffs = 0; + while(!partdone) + { + constexpr u8 tilecenter = (LCD::WIDTH * 2) / img.blockw; + u16 tiles[NextPowerOf2::value]; + u8 oldseed = 0xff; + + const u8 gxoffs = a / img.blockw; + const u8 gxshift = a % img.blockw; + + ymasteroffs = STAGGER - ymasteroffs; + + for(u16 y = ymasteroffs; y < LCD::HEIGHT; y += INTERLACE) + { + u16 line = stretches[y] - a; + u8 seed = line / BLOCK_HEIGHT; + + if(seed != oldseed) // one seed always valid for the drawn height of one block + { + oldseed = seed; + TinyRng8 xrng(seed + 1); + for(u8 i = 0; i < (u8)Countof(tiles); ++i) + tiles[i] = vmodpos(xrng()) * BLOCK_HEIGHT; + } + + s16 x = s16(xshift[y]) + s16(gxshift); + + LCD::setxy_inl(0, y, LCD::XMAX, y); + + u16 whichtile = line % BLOCK_HEIGHT; + + u16 i = u16(tilecenter) + xtileoffs[y] - gxoffs; + + if(x >= img.blockw) + { + x -= img.blockw; + --i; + } + i %= Countof(tiles); // known to be power of 2 so this is fast + + if(x) // left partial tile? unpack to temp. buffer + { + u8 buf[img.blockw]; + img.unpackBlock(buf, tiles[i] + whichtile); + u8 ofs = u8(img.blockw) - u8(x); + Draw::drawimageRaw(&buf[ofs], x); + } + ++i; + + // fill lines directly to LCD + for( ; x < LCD::WIDTH-img.blockw; ++i, x += img.blockw) + img.unpackBlock(NULL, tiles[i] + whichtile); + + if(x < LCD::WIDTH) // right partial tile? unpack to temp. buffer + { + u8 buf[img.blockw]; + img.unpackBlock(buf, tiles[i] + whichtile); + u8 todo = u8(LCD::WIDTH - x); + Draw::drawimageRaw(&buf[0], todo); + } + +#ifdef MCU_IS_PC + fglcd::delay_ms(1); +#endif + } + a += 1; + } +} diff --git a/democode/part_misc.cpp b/democode/part_misc.cpp new file mode 100644 index 0000000..09e071c --- /dev/null +++ b/democode/part_misc.cpp @@ -0,0 +1,233 @@ +#include "demoshared.h" +#include "part_c64.h" + +static void drawchess(const u32 abig, const u16 ystart, const Color fg, const u8 yinc, const u8 wobble) +{ + const u32 asmall = abig >> 2; + const u16 a = asmall >> 4; + ivec2 p(int(ISIN8FAST(a/3)*64), int(ICOS8FAST(a/2)*64)); + const s16 s = ISIN8FAST(a); + const s16 c = ICOS8FAST(a); + ivec2 step(c, s); + + for(u16 y = AREA_Y_BEGIN + ystart; y < AREA_Y_END; y += yinc) + { + ivec2 pp = p; + if(wobble) + { + pp.x += 3*scale8(ISIN8FAST(asmall/4+y), wobble); + pp.y += 4*scale8(ICOS8FAST(y-asmall/3), wobble); + } + + LCD::setxy(AREA_X_BEGIN, y, AREA_X_END, y); + for(u8 x = 0; x < AREA_W/4; ++x, pp += step) + { + const u8 SIZE = 0x08; + LCD::setColor(((uhi8(pp.x) & SIZE) ^ (uhi8(pp.y) & SIZE)) == 0 ? 0 : fg); + FGLCD_REP_4(LCD::sendPixel()); + } + p.x -= s; + p.y += c; + } +} + +struct ChessState +{ + ChessState() + : inp(16) + , beatsync { + MusicSyncVar(inp, BEATSYNC_CHANNEL1, 0, 255, 4000) + , MusicSyncVar(inp, BEATSYNC_CHANNEL2, 0, 255, 4000) + , MusicSyncVar(inp, BEATSYNC_CHANNEL3, 0, 255, 4000) + } + , wobmult(0) + , clearswipe(0) + { + done = 0; + inp.add(wobmult); + for(u8 i = 0; i < 3; ++i) + { + fadein[i].set(0); + inp.add(fadein[i]); + } + inp.add(clearswipe); + } + + ~ChessState() + { + if(done != 2) + { + done = 1; + while(done != 2) {} + } + } + + interp::Interpolator<1+3+3+1> inp; + MusicSyncVar beatsync[3]; + interp::FancyInterpolatedValue wobmult; + interp::FancyInterpolatedValue fadein[3]; + interp::FancyInterpolatedValue clearswipe; + u8vec3 colors[3]; + u16 incs[3]; + u32 accu[3]; + u8 speedmult; + u8 beginlayer; + //u8 layers; + volatile u8 stage; + volatile u8 stx; + volatile u8 done; +}; + +#define BRIGHT 133 + +static void drawchesslayers(ChessState& cs) +{ + u8 i = cs.beginlayer; + const u8 m = i; //+ cs.layers; + const u8 wobm = cs.wobmult; + u8 active = 0; + for(; i < 3; ++i) + { + u8 fade = cs.fadein[i]; + if(!fade) + continue; + ++active; + u8 a = scale8(u8(BRIGHT), fade); + u8vec3 cv = lerp(u8vec3(0), cs.colors[i], fade); + u8vec3 cmax = u8vec3( + saturateAdd(cv.r, a), + saturateAdd(cv.g, a), + saturateAdd(cv.b, a) + ); + u8 beat = cs.beatsync[i]; + const Color col = gencolor(lerp(cv, cmax, beat)); + u8 wob = wobm; + if(wob) + wob = scale8(beat, wob); + drawchess(cs.accu[i], i, col, 4, wob); + cs.accu[i] += cs.incs[i] * cs.speedmult; + } + cs.speedmult = active; +} + +static void fadein(interp::FancyInterpolatedValue& v) +{ + v.interpolateTo(255, 2500); +} + +static void fadeout(interp::FancyInterpolatedValue& v) +{ + v.interpolateTo(0, 2500); +} + +static void evt_chess(void *ud) +{ + ChessState& cs = *static_cast(ud); + if(cs.done) + { + cs.done = 2; + return; + } + u16 nexttime = 0; + u8 stage = cs.stage; + switch(stage) + { + case 0: nexttime = 2600; + cs.wobmult.interpolateTo(255, 600); + break; + case 1: nexttime = 1800; + cs.colors[0] = u8vec3(0xdf, 0, 0); + cs.colors[1] = u8vec3(0x3f, 0xdf, 0xdf); + fadein(cs.fadein[1]); + cs.accu[1] = cs.accu[0]; + break; + case 2: nexttime = 3200; + cs.colors[1] = u8vec3(0, 0, 0xdf); + cs.colors[2] = u8vec3(0x3f, 0xdf, 0x3f); + fadein(cs.fadein[2]); + cs.accu[2] = cs.accu[1]; + break; + case 3: nexttime = 1600; + fadeout(cs.fadein[2]); + break; + case 4: + fadeout(cs.fadein[1]); + break; + } + cs.stage = stage + 1; + if(nexttime) + evs::schedule(nexttime, evt_chess, ud); + else + cs.done = 2; +} + +static void evt_chessStart(void *ud) +{ + ChessState& cs = *static_cast(ud); + cs.clearswipe.interpolateTo(AREA_W / 2, 900); + evt_chess(ud); +} + +static NOINLINE void swipecol(u16 x) +{ + LCD::setxy(x, AREA_Y_BEGIN, x, AREA_Y_END); + LCD::setColor(0); + LCD::fastfill_u16(AREA_H); +} + +demopart part_layerchess() +{ + loadIsinToScratch2(); + + ChessState cs; + cs.stage = 0; + cs.stx = 0; + //cs.layers = 1; + cs.beginlayer = 0; + cs.speedmult = 1; + cs.accu[0] = 666; + cs.colors[0] = u8vec3(0xac); + cs.incs[0] = 0xf<<2; + cs.incs[1] = 0xe<<2; + cs.incs[2] = 0xd<<2; + cs.incs[3] = 0xc<<2; + fadein(cs.fadein[0]); + + /*cs.coltab[0] = LCD::gencolor(0xff, 0, 0); + cs.coltab[1] = LCD::gencolor( 0, 0xff, 0); + cs.coltab[2] = LCD::gencolor(0, 0, 0xff); + cs.coltab[3] = LCD::gencolor(0x7f, 0x7f, 0x7f);*/ + + evs::schedule(1500, evt_chessStart, &cs); + partdone = false; + u8 exit = false; + u8 xswipe = 0; + while(true) + { + const u16 xtarget = cs.clearswipe; + if(xswipe < xtarget) + { + for(u16 x = xswipe; x <= xtarget; ++x) + { + swipecol(AREA_X_BEGIN + AREA_W/2 + x); + swipecol(AREA_X_BEGIN + AREA_W/2 - x); + } + + xswipe = xtarget; + } + + drawchesslayers(cs); + if(exit) + { + if(!cs.fadein[0]) + break; + } + else if(partdone) + { + exit = true; + for(u8 i = 0; i < 3; ++i) + fadeout(cs.fadein[i]); + } + } + partdone = false; +} diff --git a/democode/part_raster.cpp b/democode/part_raster.cpp new file mode 100644 index 0000000..eb73e50 --- /dev/null +++ b/democode/part_raster.cpp @@ -0,0 +1,305 @@ +#include "demoshared.h" + +static const u8 BGW = 0x40; + +static const uint16_t pal_cube[] PROGMEM_FAR = +{ + // fg + LCD::gencolor_inl(0xff, 0x30, 0), // from part_c64 + LCD::gencolor_inl(0xaf, 0x35, 0), + LCD::gencolor_inl(0xcf, 0x45, 0), + // bg + LCD::gencolor_inl(0x4f, BGW, BGW), + LCD::gencolor_inl(0x8f, BGW, BGW), + LCD::gencolor_inl(0xdf, BGW, BGW) +}; + +struct RasterState +{ + RasterState() + { + needDone = 2; + } + ~RasterState() + { + ++done; + while(done < needDone) {} + } + raster::Params rp; + fp1616 scale; + u8 part; + u8 tickrate; + volatile u16 c; + volatile u8 done; + volatile u8 needDone; +}; + +static void ev_tick(void *ud) +{ + RasterState& s = *(RasterState*)ud; + if(s.done) + { + ++s.done; + return; + } + ++s.c; + evs::schedule(s.tickrate, ev_tick, ud); +} + +static void ev_raster(void *ud) +{ + RasterState& s = *(RasterState*)ud; + if(s.done) + { + ++s.done; + return; + } + + u16 tm = 10; + const u8 oldpart = s.part; + + if(s.part < 10) + { + if(s.part < 3) + { + if(s.scale < 330) + s.scale *= fp1616(1.02f); + else + s.part = 3; + + if(s.part == 0 && s.scale > 40) + { + s.part = 1; + + s.rp.align = 4; + //s.rp.face = 2; + s.rp.incr = 4; + //s.rp.backfaceoffs = 2; + } + + if(s.part == 1 && s.scale > 80) + { + s.rp.incr = 4; + s.part = 2; + ++s.needDone; // ticker goes now, another thread to wait for when quitting + ev_tick(ud); + } + } + else if(s.part == 3) + { + tm = 16; + constexpr fp1616 lim = 76; + if(s.scale > lim) + s.scale *= fp1616(0.993f); + else + { + s.part = 10; + s.scale = lim; + } + } + } + else + { + if(s.part == 10) + { + tm = 4444; + ++s.part; + } + else if(s.part == 11) + { + if(s.rp.rubmul < 32) + { + tm = 90; + ++s.rp.rubmul; + } + else + { + ++s.part; + tm = 3333; + } + } + else if(s.part == 12) + { + tm = 8; + /*if(s.rp.glitchmul < 0xff) + ++s.rp.glitchmul; + else*/ + s.part = 13; + } + else if(s.part == 13) + { + if(s.rp.rubmul < 90) + { + tm = 50; + ++s.rp.rubmul; + s.scale *= fp1616(1.005f); + } + else + { + ++s.part; + //tm = 500; + //DoNextPartIn(tm); + --s.needDone; // one thread less +#ifdef MCU_IS_PC + for(u8 kk = 128; kk < 255; ++kk) + palsetcolor(kk, LCD::gencolor_inl(0xff, 0, 0xff)); +#endif + return; + } + } + } + //printf("part = %u, rubmul = %u, glitchmul = %u\n", oldpart, s.rp.rubmul, s.rp.glitchmul); + evs::schedule(tm, ev_raster, ud); +} + +demopart part_c64_to_cube() +{ + clearpal(0); + + fglcd::FarPtr palpf = fglcd_get_farptr(pal_cube); + applyPal16_PF(palpf, Countof(pal_cube), 0); + applyPal16_PF(palpf, Countof(pal_cube), Countof(pal_cube)); // backup + + loadIsinToScratch2(); + loadUsinToScratch3(); + + RasterState rs; + rs.part = 0; + rs.done = 0; + rs.scale = 5; + rs.c = 0; + rs.tickrate = 20; + + rs.rp.align = 1; + rs.rp.alignoffs = 0; + //rs.rp.face = 1; + rs.rp.incr = 1; + //rs.rp.backfaceoffs = 1; // now passed to draw() + rs.rp.rubmul = 0; + rs.rp.glitchmul = 0; + //rs.rp.backfaceColorOffs = 3; + + interp::Interpolator<2> inp(16); + MusicSyncVar glitchbeat(inp, BEATSYNC_CHANNEL1, 0, 255, 3900); + interp::FancyInterpolatedValue fadeout(0); + inp.add(fadeout); + + evs::schedule(200, ev_raster, &rs); + + DrawMesh cube; + raster::ClearHelper<8> clr; + + bool exiting = false; + partdone = false; + while(true) + { + u8 glitch = glitchbeat; + brightenColors(0, glitch/2, Countof(pal_cube)); + + if(partdone && !exiting) + { + exiting = true; + fadeout.interpolateTo(255, 2400); + } + + if(exiting) + { + u8 fo = fadeout; + if(fo == 255) + break; + dampenColors(0, fo, Countof(pal_cube)); + } + + const u16 c = rs.c; + s16 bx = ISIN8FAST(c); + bx += bx/4; + s16 tx = (bx + (LCD::WIDTH / 2) - 8); + s16 ty = ((s16)ISIN8FAST(c / 2u)/2 + (LCD::HEIGHT / 2) + 4); + + mat4rz mrz(c + 32, FSinCosScratch()); + mat4rx mrx(c + (c/2), FSinCosScratch()); + mat4t mt(tx, ty, 0); + //mat4s ms(105.0f + 5 * fcos8((c/2) + (c/4))); + mat4s ms(rs.scale); + const auto m = mt * ms * mrx * mrz; + mat4ry mry(c*2, FSinCosScratch()); + const auto mm = m * mry; + + rs.rp.glitchmul = glitch; + + cube.transform(mm); + + if(rs.part >= 1) + clr.clear(0); + + raster::TriFilterResult filt = cube.filter(); + cube.draw(cube.frontFaceIdxs(), filt.nFront, rs.rp, 0, 0); + cube.draw(cube.backFaceIdxs(filt.nBack), filt.nBack, rs.rp, 1, 3); + + u8 pad = 8 + (rs.rp.rubmul >> u8(1u)); + if(rs.part >= 2 && rs.part <= 3) + pad += 13; + clr.add(cube.getAABB(), pad); + } + partdone = false; +} + +#if 0 + +demopart part_test_3dobj() +{ + fuckpal(); + loadIsinToScratch2(); + + DrawMesh mesh; + mesh.applypal(0,0,0,0); + + DrawFont font(scratch3); + + raster::Params rp; + rp.align = 2; + rp.alignoffs = 0; + //rp.face = face; + rp.incr = 2; + //rp.backfaceoffs = 0; + //rp.backfaceColorOffs = 0; + rp.rubmul = 0; + rp.glitchmul = 0; + + Camera cam(8); + cam.pos(vec3(0, 0, -10)); + cam.lookAt(vec3(0)); + + const fp1616 test = invtan88slow(8); + char buf[500]; + snprintf_P(buf, sizeof(buf), PSTR("DBG: %d:%u/%f, %f, %f, %f"), + test.intpart(), test.mantissa(), test.tofloat(), + cam.projection._dyn[0].tofloat(), + cam.projection._dyn[1].tofloat(), + cam.projection._dyn[2].tofloat() + ); + LCD::PixelPos pp(0,0); + font.template drawStr(pp, buf, 0xffff, 0); + + s16 c = 0; + + partdone = false; + while(!partdone) + { + mat4rz mrz(c + 32, FSinCosScratch()); + mat4rx mrx(c + (c/2), FSinCosScratch()); + mat4ry mry(c*2, FSinCosScratch()); + mat4s ms(3.0f); + + const auto model = ms * mrx * mrz * mry; + const auto m = cam.calcMatrix() * model; + + mesh.transform(m, ivec2(LCD::WIDTH / 2, LCD::HEIGHT / 2)); + raster::TriFilterResult filt = mesh.filter(); + uint8_t *front = mesh.frontFaceIdxs(); + mesh.draw(front, filt.nFront, rp, 0, 0); + + ++c; + } +} +#endif diff --git a/democode/part_sierp.cpp b/democode/part_sierp.cpp new file mode 100644 index 0000000..711012d --- /dev/null +++ b/democode/part_sierp.cpp @@ -0,0 +1,293 @@ +#include "src/demolib/scrollhelper.h" +#include "demoshared.h" +#include "part_c64.h" + +#ifdef __GNUC__ +#pragma GCC optimize ("Os") +#endif + +static const u8 SierpSize = AREA_H / 4 / 8; + +static const u16 USEAREAWIDTH = AREA_W+1; + +//static const u8 AUDIOFOO = pmfplayer_max_channels < SierpSize ? pmfplayer_max_channels : SierpSize; + +//static const u8 USECHANNELS = 4; + +//static_assert(USECHANNELS <= AUDIOFOO, "aa"); +//static_assert(USECHANNELS*2 <= SierpSize, "aa"); + +struct SierpScrollState : public WidescrollState +{ + volatile u8 done; + volatile u8 delay; + u16 accu; +}; + + + +static NOINLINE void updatesierp(uint8_t *p) +{ +#ifdef __AVR__ + // implements n ^= 2*n + asm volatile( + "clc \n\t" + "byteloop_%=: \n\t" + "ld r1, %a[p] \n\t" + "mov __tmp_reg__, r1 \n\t" + "rol __tmp_reg__ \n\t" // carry is needed in the next iteration + "eor r1, __tmp_reg__ \n\t" + "st %a[p]+, r1 \n\t" + "dec %[N] \n\t" + "brne byteloop_%= \n\t" + "clr r1 \n\t" + : /* out */ + : [N] "r" (SierpSize), [p] "x" (p) /* in */ + : "r0" + ); +#else + // Shitty and slow + u8 carry = 0; + for(u8 i = 0; i < SierpSize; ++i) + { + u8 v = p[i]; + u16 tmp = u16(v) << 1; + tmp += carry; + p[i] = u8(tmp) ^ v; + carry = (tmp >> 8u) & 1; + } +#endif +} + +static void drawsierpcol(const u8 * const p, u16 x, const u8 coloffs) +{ + x += AREA_X_BEGIN; + LCD::setxy_inl(x, AREA_Y_BEGIN, x, AREA_Y_END); + for(u8 k = 1; k < SierpSize; ++k) + { + u8 c = p[k]; + for(u8 i = 0; i < 8; ++i) + { + const u8 b = c & 1; + c >>= u8(1u); + LCD::sendPixel(palgetcolor_inl(b + coloffs)); + LCD::sendPixel(); + LCD::sendPixel(); + LCD::sendPixel(); + } + } +} + +struct SierpUnit +{ + u8 sierp[SierpSize]; + fp1616 shift; + u16 oldshift; + const u8 id; + + SierpUnit(u8 id) + : shift(0), oldshift(0), id(id) + { + } + + void reset() + { + fglcd::RAM::Memset(sierp, 0, sizeof(sierp)); + sierp[0] = 1; + } + + void update(fp1616 add) + { + u16 sh = shift.intpart(); + shift += add; + shift.f.part.hi = vmodpos(shift.f.part.hi); + sh &= ~3; + if(sh == oldshift) + return; + oldshift = sh; + sh += id; + u8 color = id << 1; + for(u16 xx = sh; xx < USEAREAWIDTH; xx += 4) + { + updatesierp(sierp); + drawsierpcol(sierp, xx, color); + } + for(u16 xx = id; xx < sh; xx += 4) + { + updatesierp(sierp); + drawsierpcol(sierp, xx, color); + } + + } +}; + +struct SierpState +{ + SierpUnit s2, s3, s4; + SierpScrollState scroll; + + SierpState() : s2(1), s3(2), s4(3) + { + fglcd::RAM::Memset(&scroll, 0, sizeof(scroll)); + scroll.scrollpos = LCD::WIDTH; + scroll.direction = 4; + scroll.delay = 45; + } +}; + +static void dbg_cb(DebugThing& dbg, void *ud, u8 b, u8 bx) +{ + SierpState& ss = *((SierpState*)ud); + + dbg.set2Num(ss.scroll.direction, ss.scroll.delay); + + if(b & bx & 1) + ss.scroll.flip(); + if(b & bx & 2) + --ss.scroll.direction; + if(b & bx & 4) + ++ss.scroll.direction; + if(b & bx & 8) + --ss.scroll.delay; + if(b & bx & 16) + ++ss.scroll.delay; +} + +static void ev_scroll(void *ud) +{ + SierpState& ss = *((SierpState*)ud); + if(ss.scroll.done) + { + ++ss.scroll.done; + return; + } + + u16 spmod = ss.scroll.scroll(ss.scroll.direction); + ss.scroll.accu += ss.scroll.direction; + + { + LCD::StateBackup rti; + FGLCD_ASSERT_VAL(spmod < USEAREAWIDTH, "sierpspm", spmod); + spmod += AREA_X_BEGIN; + LCD::set_scroll_pos(spmod); + } + + evs::schedule(ss.scroll.delay, ev_scroll, ud); +} + +static const Color sierppal_PF[] PROGMEM_FAR = +{ + LCD::gencolor_inl(0xff, 0xbf, 0), + LCD::gencolor_inl(0x56, 0x69, 0xdf), + LCD::gencolor_inl(0x36, 0x69, 0x2f), + LCD::gencolor_inl(0x86, 0x39, 0x9f), +}; + +static void sierpsync(uint8_t channel, pmf_channel_info info, void *ud) +{ + SierpState *state = (SierpState*)ud; +} + +void part_sierpscroll() +{ + clearpal(0); + LCD::set_scroll_pos(AREA_X_BEGIN); + + constexpr unsigned areaw = + LCD::CONST_scroll_area_sides(AREA_X_BEGIN, LCD::XMAX-AREA_X_END); + LCD:: set_scroll_area_sides(AREA_X_BEGIN, LCD::XMAX-AREA_X_END); + static_assert(areaw == AREA_W, "areawwtf"); + + //loadIsinToScratch2(); + //loadUsinToScratch3(); + //LCD::set_refresh_rate(LCD::HZ_125); + + u8 sierp1[SierpSize]; + SierpState ss; + //u8 sierp2base[SierpSize]; + fglcd::RAM::Memset(sierp1, 0, sizeof(sierp1)); + //fglcd::RAM::Memset(sierp2base, 0, sizeof(sierp2base)); + sierp1[0] = 1; + //sierp2base[0] = 1; + DEBUG_THING(dbg_cb, &ss); + + //musync::Override mu(BEATSYNC_CHANNEL1, sierpsync, &ss, musync::CallOnHit); + + /*u8 cx = 0; + do + { + palsetcolor(cx, 0); + palsetcolor(cx+1, 0xffff); + } + while(cx += 2);*/ + + const u8 NP = Countof(sierppal_PF); + u8vec3 palv[NP], palvbright[NP]; + { + auto pal = farload(sierppal_PF); + palsetcolor(1, pal[0]); + for(u8 i = 0; i < NP; ++i) + { + u8 v[3]; + LCD::splitcolor(pal[i+1], &v[0]); + for(u8 k = 0; k < 3; ++k) + { + palvbright[i][k] = saturateAdd(v[k], 90); + palv[i][k] = saturateSub(v[k], 30); + } + } + } + + interp::Interpolator<4> inp(16); + MusicSyncVar beat1(inp, BEATSYNC_CHANNEL1, palv[0], palvbright[0], 3333); + MusicSyncVar beat2(inp, BEATSYNC_CHANNEL2, palv[1], palvbright[1], 3333); + MusicSyncVar beat3(inp, BEATSYNC_CHANNEL3, palv[2], palvbright[2], 3333); + interp::FancyInterpolatedValue fadein(0); + inp.add(fadein); + fadein.interpolateTo(255, 1000); + evs::schedule(100, ev_scroll, (void*)&ss); + + partdone = false; + u8 rpos = 0; + u16 shift2 = 0; + fp1616 shift3 = 108, shift4 = 44; + while(!partdone) + { + const u8 fade = fadein; + palsetcolor(3, gencolorScaled(beat1, fade)); + palsetcolor(5, gencolorScaled(beat2, fade)); + palsetcolor(7, gencolorScaled(beat3, fade)); + + while(rpos != ss.scroll.wpos) + { + const s16 sp = ss.scroll.getDrawCol(rpos++); + if((sp & 3) == 0) + { + const u16 x = vmodpos(sp); + updatesierp(sierp1); + drawsierpcol(sierp1, x, 0); + } + } + + LCD::set_address_mode(LCD::ADDRMODE_BT_RL); + ss.s2.reset(); + ss.s2.update(0.5f*4); + + LCD::set_address_mode(LCD::ADDRMODE_LANDSCAPE); + ss.s3.reset(); + ss.s3.update(2*3.3f); + + LCD::set_address_mode(LCD::ADDRMODE_BT_RL); + ss.s4.reset(); + ss.s4.update(2*2.6f); + + LCD::set_address_mode(LCD::ADDRMODE_LANDSCAPE); + + } + ss.scroll.done = 1; + while(ss.scroll.done == 1) {}; + //LCD::set_refresh_rate(LCD::HZ_DEFAULT); + + LCD::set_scroll_area_sides(0, 0); + LCD::set_scroll_pos(0); +} diff --git a/democode/part_test.cpp b/democode/part_test.cpp new file mode 100644 index 0000000..1d3bef8 --- /dev/null +++ b/democode/part_test.cpp @@ -0,0 +1,148 @@ +// Unfinished test crap goes here + +#include "demoshared.h" +#include "rotozoom.h" + +demopart part_strobo() +{ + for(;;) + { + LCD::clear(0); + LCD::clear(0xffff); + } +} + +void scrolltest() +{ + LCD::clear(0); + LCD::set_scroll_area_sides(0, 0); + //LCD::set_refresh_rate(LCD::HZ_125); + + LCD::fillrect(0, 0, 100, LCD::HEIGHT, 0xffff); + + u8 scroll = 0; + for(;;) + { + LCD::set_scroll_pos(scroll); + ++scroll; + //_delay_ms(1); + LCD::soft_reset(); + } +} + +static void evt_reset(void*) +{ + LCD::setColor(0); + partdone = true; +} + +static void timingtest() +{ + // This shows the total number of pixels that can be drawn in 1 ms + for(;;) + { + partdone = false; + LCD::preparefill(); + LCD::setColor(0xfff); + evs::schedule(1, evt_reset, NULL); + LCD::fastfill_inl(LCD::TOTAL_PIXELS); + while(!partdone) {} + } +} + +demopart test_showwaveform() +{ + partdone = false; + interp::Interpolator<4> derp(16); + + /*interp::FancyInterpolatedValue icol; + derp.add(icol); + icol.interpolateTo(0xff, 500);*/ + + MusicSyncVar icol(derp, BEATSYNC_CHANNEL1, 0, 255, 3000); + + const u16 maxHalfX = vmin(CFG_PMFPLAYER_AUDIO_BUFFER_SIZE, LCD::WIDTH / 2) - 2; + while(!partdone) + { + u8 w = icol; + Color col = LCD::gencolor(w,w,w); + //printf("%d\n", col); + for(u16 i = 1; i < maxHalfX; ++i) + { + u16 x = i+i; + LCD::setxywh(x, 0, 1, LCD::YMAX); + s16 t = music_curSample16(u8(i)); + if(t >= 0) + { + LCD::setColor(0); + LCD::fastfill_u8(64); + t = vmin(t, 64); + const u8 w = (u8)t; + LCD::setColor(col); + LCD::fastfill_u8(w); + LCD::setColor(0); + LCD::fastfill_u8(64-w); + } + else + { + t = vmin(-t, 64); + const u8 w = (u8)t; + LCD::setColor(0); + LCD::fastfill_u8(64-w); + LCD::setColor(col); + LCD::fastfill_u8(w); + LCD::setColor(0); + LCD::fastfill_u8(64); + } + } + } +} + +// Hi :D +// -slerpy + +void test_showimage() +{ + DecompImageData img; + img.applypal(); + Draw::drawimageRaw(0, 0, img.w, img.h, img.ptr()); +} + +void test_rotozoom() +{ + rotozoom::Helper rz; + u16 a = 0; + while(!partdone) + { + rz.draw(a); + a += 55; + } +} + +/* +void partmissing(const char *name_P, u16 ms, bool wait) +{ + u16 oldms = ms; + DrawFont font(scratch3); + char name[32]; + strncpy_P(name, name_P, sizeof(name)-1); + char buf[128]; + LCD::PixelPos pp; + pp.x = 80; + pp.y = 80; + while(!partdone) + { + u16 s = ms / 1000; + u16 rms = ms - (s * 1000); + snprintf_P(buf, sizeof(buf), PSTR("[%s]\nPart missing (%u ms)!\n%u.%u remain...\n[wait=%u]"), + name, oldms, s, rms / 100, wait); + font.template drawStr(pp, buf, 0xffff, 0); + + fglcd::delay_ms(100); + ms = vmin(ms, ms - 100); + if(!ms && !wait) + break; + } + partdone = false; +} +*/ \ No newline at end of file diff --git a/democode/part_twist.cpp b/democode/part_twist.cpp new file mode 100644 index 0000000..804dbb8 --- /dev/null +++ b/democode/part_twist.cpp @@ -0,0 +1,379 @@ +#include "demoshared.h" +#include "part_twist.h" + +static const u8 BASE_THICKNESS = 111; + +struct TwistParamLerp +{ + interp::Interpolator<8> inp; + MusicSyncVar beat; + interp::FancyInterpolatedValue + iTwistMult, iRotMult, iWobMult, iSoundWobMult, iWobThick, iWobShrink, iColorSub; + + volatile u8 state, done, threadDone; + + TwistParamLerp() + : inp(16) + , beat(inp, BEATSYNC_CHANNEL1, 0, 40, 3000) + , iRotMult(0), iTwistMult(0), iWobMult(0), iSoundWobMult(0), iWobThick(0), iWobShrink(0) + , iColorSub(255) + , state(0) + , done(0), threadDone(0) + { + inp.add(iRotMult); + inp.add(iTwistMult); + inp.add(iWobMult); + inp.add(iSoundWobMult); + inp.add(iWobThick); + inp.add(iWobShrink); + inp.add(iColorSub); + iColorSub.interpolateTo(0, 500); + + evs::schedule(3000, ev_tick, this); + } + + ~TwistParamLerp() + { + done = 1; + while(!threadDone) {} + } + + uint16_t next() + { + if(done) + return 0; +#ifdef MCU_IS_PC + printf("twist state = %u\n", state); +#endif + + /* progression: + - start flat + - rotate all across + - start to twist + - start to wobble + - mix in music + */ + switch(state++) + { + case 0: + iWobThick.interpolateTo(240, 1200); + iWobShrink.interpolateTo(BASE_THICKNESS - 30, 500); + return 2000; + case 1: + iWobThick.interpolateTo(0, 600); + iWobShrink.interpolateTo(0, 600); + return 1500; + case 2: + iRotMult.interpolateTo(150, 300); + return 2000; + case 3: + iTwistMult.interpolateTo(0xff, 100); + return 2000; + case 4: + iWobMult.interpolateTo(100, 800); + return 2000; + case 5: + iSoundWobMult.interpolateTo(64, 300); + return 2000; + } + +#ifdef MCU_IS_PC + LCD::fillrectfromto(0,0,30,30, LCD::gencolor_inl(0xff,0,0xff)); +#endif + return 0; + } + + static void ev_tick(void *ud) + { + TwistParamLerp *tpl = (TwistParamLerp*)ud; + uint16_t t = tpl->next(); + if(t) + evs::schedule(t, ev_tick, ud); + else + tpl->threadDone = 1; + } + + void apply(TwistParams& p) + { + p.xRotMult = iTwistMult; + p.aRotMult = iRotMult; + p.wobbleMult = iWobMult; + p.soundWobMult = iSoundWobMult; + p.baseThickness = BASE_THICKNESS + beat - iWobShrink; + p.wobThickness = iWobThick; + //p.twistMult = iRotMult; + } +}; + +static void startparams(TwistParams& p) +{ + p.xRotMult = 0; + p.aRotMult = 0; + p.wobbleMult = 0; + p.soundWobMult = 0; + p.baseThickness = BASE_THICKNESS; + p.wobThickness = 0; + p.twistMult = 64; +} + +static void testparams(TwistParams& p) +{ + /* + p.xRotMult = 0xff; + p.aRotMult = 0xff; + p.wobbleMult = 100; + p.soundWobMult = 64; + p.baseThickness = 127; + p.wobThickness = 0; + p.twistMult = 64; + */ + + p.xRotMult = 0xff; + p.aRotMult = 0xff; + p.wobbleMult = 100; + p.soundWobMult = 0; + p.baseThickness = 127; + p.wobThickness = 0; + p.twistMult = 64; +} + + + +TwistState::TwistState(unsigned a_, unsigned r_) + : a(a_), r(r_) + +{ + startparams(p); + fglcd::RAM::Memset(&yy, 0, sizeof(yy)); +} + +struct DrawLineInfo +{ + u16 sx; + u8 n, pal, angle; +}; + +static void drawside(const DrawLineInfo& dr, s16 s, u8 colorsPerSide) +{ + u8 k = dr.n; // full length of side to draw, in screen space + + const u8 palidx = dr.angle >> 2; + const u8 palbase = colorsPerSide*dr.pal; + const Color bgcol = palgetcolor_inl(palbase + palidx); + + if(!s) + { + LCD::setColor(bgcol); + LCD::fastfill_u8(k); + return; + } + + const Color wavcol = palgetcolor_inl(palbase + vmax(((palidx>>1) - 8), 1)); + + const u8 half = k / 2u; // half side + k -= half; // k is now the maximum amplitude reachable + + /*k h + a ## h + a ####### h + a ------##----- b + h ##### b + h ## k + h # k + */ + + if(s > 0) + { + // upper wave + u8 a = uhi8(dr.angle * s); + if(a < k) + { + k -= a; // k is now space between wave and amplitude + LCD::setColor(bgcol); + LCD::fastfill_u8(k); + } + else + a = half; + LCD::setColor(wavcol); + LCD::fastfill_u8(a); + } + + LCD::setColor(bgcol); // the empty half + LCD::fastfill_u8(half); + + if(s < 0) + { + // lower wave + u8 b = uhi8(dr.angle * -s); + if(b < k) + { + k -= b; + LCD::setColor(wavcol); // lower half + LCD::fastfill_u8(b); + } + else + b = half; + + LCD::setColor(bgcol); + LCD::fastfill_u8(k); + } +} + +#define CPS(a, b) if(b.sx < a.sx) vswap(a, b) + +void drawtwisthoriz(TwistState& ts, uint8_t xstart, uint8_t xinc, s8 yoffs, Color bordercol) +{ + u16 r = ts.r; + const u16 a = (u32(ts.a) * ts.p.twistMult) >> 8; + const u16 wobval = a+a/2; + const u8 colorsPerSide = ts.colorsPerSide; + u8 mm = 0; + + s16 lastsample = music_curSample16(mm); + for(u16 x = xstart; x < LCD::WIDTH; x += xinc, ++r, ++mm) + { + // MUSICSYNC + s16 s = music_curSample16(mm); + + // very rudimentary sample smoothing + s16 ls = lastsample; + lastsample = s; + s += ls; + s >>= 1; + + // no idea wtf this does but this looks good enough + const u8 rh = uhi8(r); + s16 xfactor = ISIN8FAST(rh + x/3 + ICOS8FAST(rh)); + xfactor -= ICOS8FAST((x/2)+rh)/2; + xfactor -= ISIN8FAST(rh-u8(a/2)); + + s16 t = hiscale8((u8)a, ts.p.aRotMult); + t += (xfactor * ts.p.xRotMult) >> 8; + const s8 q1 = ISIN8FAST((u8)t); + const s8 q2 = ICOS8FAST((u8)t); + u8 bt = ts.p.baseThickness; + if(u8 wt = ts.p.wobThickness) + bt += uhi8(vabs(s) * ts.p.wobThickness); + const s8 w1 = hiscale8(q1, bt); + const s8 w2 = hiscale8(q2, bt); + const u16 pos = u16(LCD::HEIGHT-(w1+w2))/2; + DrawLineInfo dr[2]; + u8 dri = 0; + if(w1 > 0) + dr[dri++] = {u16(pos), (u8)w1, 0, (u8)q1 }; + if(w2 > 0) + dr[dri++] = { u16(pos+w1), (u8)w2, 1, (u8)q2 }; + if(w2 < 0) + dr[dri++] = { u16(pos+w2), (u8)-w2, 2, (u8)-q2 }; + if(w1 < 0) + dr[dri++] = { u16(pos+w2+w1), (u8)-w1, 3, (u8)-q1}; + + if(dri > 1) + CPS(dr[0], dr[1]); + + u16 y = yoffs + dr[0].sx; + if(u8 wobble = ts.p.wobbleMult) + { + s8 morewob = uhi8(s * ts.p.soundWobMult); + incrhiscale(y, ICOS8FAST(wobval+x/2+morewob), wobble); + } + if(y > LCD::HEIGHT) // underflow? + y = 0; + const u16 prevy = ts.yy.upper[mm]; + ts.yy.upper[mm] = y; + + if(prevy < y) + { + // erase the old crap + LCD::setxy(x, prevy, x, LCD::YMAX); + LCD::setColor(0); + LCD::fastfill_u8(y - prevy); + } + else + LCD::setxy(x, y, x, LCD::YMAX); + + // --- first side --- + + LCD::sendPixel(bordercol); LCD::sendPixel(); + y += 2; + + drawside(dr[0], s, colorsPerSide); + y += dr[0].n; + + // --- second side, if visible --- + if(dri > 1) + { + LCD::sendPixel(bordercol); LCD::sendPixel(); + y += 2; + + drawside(dr[1], s, colorsPerSide); + y += dr[1].n; + } + + LCD::sendPixel(bordercol); LCD::sendPixel(); + y += 2; + + u16 oldyend = ts.yy.lower[mm]; + ts.yy.lower[mm] = y; + if(y < oldyend) + { + // erase the rest + LCD::setColor(0); + u8 d = oldyend - y + 2; // HACK: +2 fixes stray pixels + LCD::fastfill_u8(d); + } + } + ts.r = r; +} + +const Color twistpal_PF[] PROGMEM_FAR = { LCD::gencolor(0xff, 0, 0), LCD::gencolor(0, 0xff, 0), LCD::gencolor(0, 0, 0xff), LCD::gencolor(0xff, 0, 0xff) }; + + +void part_twist() +{ + loadIsinToScratch2(); + loadUsinToScratch3(); + + TwistParamLerp twl; + + auto twistpal = farload(twistpal_PF); + + // LCD::set_refresh_rate(LCD::HZ_125); + TwistState s {0, 0}; + u8 lastColorSub = 0; + + partdone = false; + + bool exiting = false; + + const u8 maxHalfX = vmin(CFG_PMFPLAYER_AUDIO_BUFFER_SIZE, LCD::WIDTH / 2) - 2; + const u8 BHALF = 90; + while(true) + { + u8 sub = twl.iColorSub; + if(exiting && sub==255) + break; + if(sub != lastColorSub) + { + lastColorSub = sub; + + Color tmp[Countof(twistpal_PF)]; + for(u8 i = 0; i < Countof(twistpal_PF); ++i) + tmp[i] = dampenColor(twistpal[i], sub); + s.initpal(&tmp[0], (0xff-sub) / 25, 32); + } + if(partdone) + { + if(!exiting) + twl.iColorSub.interpolateTo(255, 2000); + exiting = true; + } + twl.apply(s.p); + + ++s.a; + drawtwisthoriz(s, 0, 2); + } + // LCD::set_refresh_rate(LCD::HZ_DEFAULT); + + partdone = false; +} diff --git a/democode/part_twist.h b/democode/part_twist.h new file mode 100644 index 0000000..4c318f2 --- /dev/null +++ b/democode/part_twist.h @@ -0,0 +1,44 @@ +#pragma once + +#include "demoshared.h" + +struct TwistParams +{ + uint8_t xRotMult; // the twist + uint8_t aRotMult; + uint8_t wobbleMult; // how much the thing is a static sinewave + uint8_t soundWobMult; + uint8_t baseThickness; + uint8_t wobThickness; + uint8_t twistMult; // overall rotation +}; + +struct TwistState +{ + TwistParams p; + unsigned a, r; + uint8_t colorsPerSide; + + struct + { + u16 upper[LCD::WIDTH]; // store upper and lower y positions for clearing + u16 lower[LCD::WIDTH]; + } yy; + + + TwistState(unsigned a, unsigned r); + + FORCEINLINE void initpal_PF(fglcd::FarPtr p, uint8_t mul, uint8_t colorsPerSide_, uint8_t paloffs = 0) + { + shadepal_PF(colorsPerSide_ / 2, mul, paloffs, p, 4); + colorsPerSide = colorsPerSide_; + } + + FORCEINLINE void initpal(const Color *p, uint8_t mul, uint8_t colorsPerSide_, uint8_t paloffs = 0) + { + shadepal(colorsPerSide_ / 2, mul, paloffs, p, 4); + colorsPerSide = colorsPerSide_; + } +}; + +void drawtwisthoriz(TwistState& s, uint8_t xstart, uint8_t xinc, s8 yoffs = 0, Color bordercol = 0xffff); diff --git a/democode/part_widescroll.cpp b/democode/part_widescroll.cpp new file mode 100644 index 0000000..48181c5 --- /dev/null +++ b/democode/part_widescroll.cpp @@ -0,0 +1,150 @@ +#include "demoshared.h" + +#ifdef __GNUC__ +#pragma GCC optimize ("Os") +#endif + +typedef DecompStitchedImageBlocks TheScrollPic; + + +struct ScrollState : public WidescrollState +{ + volatile uint8_t done; + //volatile uint8_t scrollmode; + volatile uint8_t rpos; + uint16_t accu; + + interp::Interpolator<1> inp; + interp::FancyInterpolatedValue scrolltimer; // less is faster +}; + + +static void ev_widescroll(void *ud) +{ + fglcd::ProgmemFar::SegmentBackup zz; + + ScrollState& state = *((ScrollState*)ud); + if(state.done) + { + ++state.done; + return; + } + + u16 spmod; + //if(state.scrollmode) + { + //const s16 s = ISIN8FAST(state.accu >> 2u); + constexpr fp1616 fuckingmagicnumber = fp1616(0.111f); + const fp1616 q = fuckingmagicnumber * state.accu; + const s16 s = ISIN8FAST(q.intpart()); + spmod = state.smoothscroll(s * 8); + } + /*else + { + spmod = state.scroll(state.direction); + }*/ + state.accu += state.direction; + + { + LCD::StateBackup rti; + LCD::set_scroll_pos(spmod); + } + u16 t = state.scrolltimer; + evs::schedule(state.scrolltimer, ev_widescroll, ud); +} + +static void ev_widescrollstart(void *ud) +{ + ScrollState& state = *((ScrollState*)ud); + state.scrolltimer.set(24); + state.scrolltimer.interpolateTo(5, 40); + state.inp.add(state.scrolltimer); + state.inp.start(16); + ev_widescroll(ud); +} + +static void dbg_cb(DebugThing& dbg, void *ud, u8 b, u8 bx) +{ + fglcd::ProgmemFar::SegmentBackup zz; + + ScrollState& state = *((ScrollState*)ud); + /*const s16 sp = state.scrollpos; + const u16 x = vmodpos(sp); + const u16 ix = vmodpos(sp); + + dbg.set2Num(x, ix);*/ + + uint8_t w = state.wpos; + uint8_t r = state.rpos; + uint8_t d = w-r; +#ifdef __AVR__ + uint8_t z = RAMPZ; +#else + uint8_t z = 0xff; +#endif + + dbg.set2Num(z, d); + + if(!bx) + return; + + if(b & bx & 1) + { + state.flip(); + } + if(b & bx & 2) + --state.direction; + if(b & bx & 4) + ++state.direction; + //if(b & bx & 8) + // state.scrollmode = !state.scrollmode; +} + +demopart part_widescroll() +{ + loadIsinToScratch2(); + //LCD::set_refresh_rate(LCD::HZ_125); + + TheScrollPic img; + img.applypal(); + + for(u16 x = 0; x < LCD::WIDTH; ++x) + { + LCD::setxy(x, 0, x, LCD::YMAX); // one column + img.toLCD(x); + } + + ScrollState state; + fglcd::RAM::Memset((void*)&state, 0, sizeof(state)); + state.scrollpos = LCD::WIDTH; + state.direction = 1; + new (&state.inp) interp::Interpolator<1>; // unfortunately the memset fucks this + + DEBUG_THING(dbg_cb, &state); + + evs::schedule(500, ev_widescrollstart, (void*)&state); + + u8 rpos = 0; + partdone = false; + while(!partdone) + { + const u8 wpos = state.wpos; + state.rpos = rpos; + while(rpos != wpos) + { + const u16 sp = state.getDrawCol(rpos++); + const u16 x = vmodpos(sp); + LCD::setxy(x, 0, x, LCD::YMAX); // one column + + const u16 ix = vmodpos(sp); + img.toLCD(ix); + } + } + state.done = 1; + + LCD::clear(0); + //LCD::set_refresh_rate(LCD::HZ_DEFAULT); + LCD::set_scroll_pos(0); + + while(state.done == 1) {}; +} diff --git a/democode/part_wolf.cpp b/democode/part_wolf.cpp new file mode 100644 index 0000000..a325c73 --- /dev/null +++ b/democode/part_wolf.cpp @@ -0,0 +1,120 @@ +#include "demoshared.h" + +static void ev_lerpin(void *ud) +{ + interp::FancyInterpolatedValue *colmul = (interp::FancyInterpolatedValue*)ud; + colmul->interpolateTo(255, 800); +} + +demopart part_wolf() +{ + loadIsinToScratch2(); + + DrawImageHelper img; + img.applypal(); + img.draw(0, 0); + + DrawMesh mesh; + raster::Params rp; + rp.align = 2; + rp.alignoffs = 0; + //rp.face = 1; + rp.incr = 3; + //rp.backfaceoffs = 1; + rp.rubmul = 0; + rp.glitchmul = 0; + + partdone = 0; + u16 q = 0; + + const u8vec3 mehv(0x33,0x33,0x66); + const u8vec3 mehv2(0x0,0x44,0x22); + + raster::ClearHelper<4> clr; + const ivec2 meshpos(int(img.w)-8, int(img.h /2)-16); + + interp::Interpolator<4> ilp(16); + MusicSyncVar syncx(ilp, BEATSYNC_CHANNEL1, 0, 0xC00, 1800); + MusicSyncVar synccol1(ilp, BEATSYNC_CHANNEL2, mehv, u8vec3(80,80,80), 3000); + MusicSyncVar synccol2(ilp, BEATSYNC_CHANNEL3, u8vec3(99,99,255), u8vec3(255,255,255), 4000); + interp::FancyInterpolatedValue colmul(0); + ilp.add(colmul); + evs::schedule(1000, ev_lerpin, &colmul); + + u8 exiting = false; + while(true) + { + const u8 cm = colmul; + + // start fading out the thing + if(!exiting && partdone) + { + exiting = true; + colmul.interpolateTo(0, 1000); + } + + const Color meh = LCD::gencolor_inl( + scale8(mehv.r, cm), + scale8(mehv.g, cm), + scale8(mehv.b, cm)); + const Color meh2 = LCD::gencolor_inl( + scale8(mehv2.r, cm), + scale8(mehv2.g, cm), + scale8(mehv2.b, cm)); + palsetcolor(0, meh); + palsetcolor(1, meh2); + + { + uint16_t plopx = syncx; + fp1616 scalex = fp1616::raw(17 + uhi8(plopx), ulo8(plopx) << 8); + mat4rx mrx(q, FSinCosScratch()); + mat4rz mrz(q/2, FSinCosScratch()); + mat4ry mry(q/3, FSinCosScratch()); + mat4s ms(scalex); + const auto m = ms * mrx * mrz * mry; + mesh.transform(m, meshpos); + } + + clr.add(mesh.getAABB(), 3); + clr.clear(0); + + raster::TriFilterResult filt = mesh.filter(); + mesh.draw(mesh.frontFaceIdxs(), filt.nFront, rp, 0, 0); + + //Color bgcol = LCD::gencolor_inl(80,80,80); // --> lerp to meh + //Color blue = LCD::gencolor_inl(99,99,255); // --> lerp white to this + Color bgcol = gencolorScaled(synccol1, cm); + Color blue = gencolorScaled(synccol2, cm); + + if(bgcol != meh) + { + const uint8_t *back = mesh.backFaceIdxs(filt.nBack); + mesh.drawWireframe(back, filt.nBack, bgcol); + } + + const uint8_t *front = mesh.frontFaceIdxs(); + mesh.drawWireframe(front, filt.nFront, blue); + + ++q; + + if(!cm && exiting) + break; + } + + + // move away + fp1616 d = 14; + for(u16 x = 0; x < LCD::WIDTH; ++x) + { + LCD::setxy(x, 0, x, LCD::YMAX); + LCD::setColor(0); + LCD::fastfill_u16(LCD::HEIGHT); + LCD::set_scroll_pos(x); + u16 dd = d.intpart(); + while(dd--) + fglcd::delay_ms(1); + d *= fp1616(0.99f); + } + + LCD::set_scroll_pos(0); +} diff --git a/democode/rotozoom.h b/democode/rotozoom.h new file mode 100644 index 0000000..da138d1 --- /dev/null +++ b/democode/rotozoom.h @@ -0,0 +1,75 @@ +#pragma once + +#include "demoshared.h" + +namespace rotozoom { + +static FORCEINLINE fp1616 sinval(u16 a) +{ + return FSmoothSinCosScratch::sin(fp88::raw(a)); +} + +static FORCEINLINE fp1616 cosval(u16 a) +{ + return FSmoothSinCosScratch::cos(fp88::raw(a)); +} + +#define ROTO_SIZE 6 // also adjust the FGLCD_REP_xx below to whatever this is + +template +static FORCEINLINE void draw_vert(typename ImageData::Mem::Pointer image, u16 a) +{ + constexpr u16 W = ImageData::w; + constexpr u16 H = ImageData::h; + typedef typename ImageData::Mem Mem; + + constexpr fp1616 zoommult = fp1616(0.9f); + constexpr fp1616 sizemult = fp1616(0.4f); + constexpr fp1616 panmult = fp1616(32); + const u16 a3 = a * 3; + + const fp1616 dy = panmult * sinval(a + a3); + const fp1616 dx = panmult * cosval(a*2); + vec2 p(dx, dy); + + const fp1616 s = sinval(a) * sizemult - sinval(a3) * zoommult; + const fp1616 c = cosval(a) * sizemult - cosval(a3) * zoommult; + const svec2 step(c.tofp88(), s.tofp88()); + + for(u16 x = 0; x < LCD::WIDTH; x += ROTO_SIZE) + { + svec2 pp(p.x.tofp88(), p.y.tofp88()); + LCD::setxy(x, 0, x, LCD::YMAX); + for(u8 x = 0; x < LCD::HEIGHT/ROTO_SIZE; ++x, pp += step) + { + const u8 xx = pp.x.intpart(); + const u8 yy = pp.y.intpart(); + const u16 datapos = (yy & (H-1)) * W + (xx & u8(W-1)); + const u8 idx = Mem::template read(image + datapos); + LCD::setColor(palgetcolor_inl(idx)); + FGLCD_REP_6(LCD::sendPixel()); + } + p.x += s; + p.y -= c; + } +} + +template +struct Helper +{ + typedef DecompImageData ImageData; + ImageData img; + + Helper() + { + loadIsinToScratch2(); + applyPal16_Image(); + } + + void draw(u16 a) + { + draw_vert(img.ptr(), a); + } +}; + +} // end namespace rotozoom diff --git a/democode/src/demolib/_algo_sort.h b/democode/src/demolib/_algo_sort.h new file mode 100644 index 0000000..b7d3acc --- /dev/null +++ b/democode/src/demolib/_algo_sort.h @@ -0,0 +1,84 @@ +#pragma once + +#include + +namespace fgstd { +namespace priv { + +template +ITER pickcenterelem(const ITER& left, const ITER& right) +{ + return left + ((right - left + 1) / 2u); +} + +template +void medianswap3(const ITER& a, const ITER& b, const ITER& c, const LESS& lt, const SWAPPER& swp) +{ + if(lt(*b, *a)) + swp(*b, *a); + if(lt(*c, *b)) + swp(*c, *b); + if(lt(*b, *a)) + swp(*b, *a); +} + +template +ITER partition(ITER left, ITER right, const LESS& lt, const SWAPPER& swp) +{ + ITER pivot = pickcenterelem(left, right); + medianswap3(left, pivot, right, lt, swp); + + const auto pivotval = *pivot; + while(true) + { + do + ++left; + while(lt(*left, pivotval)); + do + --right; + while(lt(pivotval, *right)); + if(left < right) + swp(*left, *right); + else + return right; + } +} + + +template +void quicksort(ITER left, ITER right, const LESS& lt, const SWAPPER& swp) +{ + while(left < right) + { + switch(right - left) + { + case 1: + if(lt(*right, *left)) + swp(*left, *right); + return; + case 2: + medianswap3(left, left + 1, right, lt, swp); + return; + } + ITER mid = partition(left, right, lt, swp); + + if(mid - left < right - mid) + { + quicksort(left, mid, lt, swp); // recurse into smaller left half + left = mid + 1; // "tail recurse" into bigger right half + } + else + { + quicksort(mid + 1, right, lt, swp); // recurse into smaller right half + right = mid; // "tail recurse" into bigger left half + } + } +} + + + +} // end namespace priv + + + +} // end namespace fgstd diff --git a/democode/src/demolib/cfg-demo.h b/democode/src/demolib/cfg-demo.h new file mode 100644 index 0000000..3b4dd47 --- /dev/null +++ b/democode/src/demolib/cfg-demo.h @@ -0,0 +1,73 @@ +#pragma once + +#include "fglcd/fglcd.h" +#include "fglcd/chip_tm1638.h" + + +typedef fglcd::Pin MegaPin5; +typedef fglcd::Pin MegaPin6; +typedef fglcd::Pin MegaPin7; +typedef fglcd::TM1638 Panel; + +typedef fglcd::preset::ILI9481_Mega_Shield LCD; + +typedef LCD::ColorType Color; + +#define CFG_MAXRAM 8192 // in bytes +#define CFG_RAM_SAFETY_AREA 256 // in bytes. Attempt to keep this much RAM free for interrupts, stack, etc. + +#define CFG_SCRATCH_MAX_AREAS 4 // 0/undef to disable scratch +#define CFG_SCRATCH_BLOCK_SIZE 256 +#define CFG_SCRATCH_ALIGNMENT 256 + +#define CFG_PAL_SIZE 256 +#define CFG_PAL_START_LO (scratch0) +#define CFG_PAL_START_HI (scratch1) // must be directly located behind CFG_PAL_START_LO in memory + +#define CFG_PTR_ISINTAB (scratch2) +#define CFG_PTR_USINTAB (scratch3) + +#define CFG_PTR_DECOMPWINDOW (scratch3) + +#define CFG_MAX_EVENTS 8 // 0/undef to disable event system +#define CFG_USE_HIGH_PRECISION_EVENT_TIMER 0 + +#define CFG_ENABLE_AUDIO +#define CFG_MUSIC_SYNC_CHANNELS 4 // undef to disable music sync +#define CFG_PMFPLAYER_AUDIO_BUFFER_SIZE 256 +#define CFG_PMFPLAYER_SAMPLING_RATE 13000 + +#define CFG_FAST_TRIANGLE_SORTING 1 // 0/undef to disable; fast sorting needs alloca() and some extra bytes +//#define CFG_FLOAT_DEPTH 0 // 0/undef to disable; only for debugging! + +#ifdef FGLCD_DEBUG +#define CFG_USE_DEBUG_THING +#endif + + +/* +typedef fglcd::preset::ILI9486_Uno_Shield LCD; + +typedef fglcd::Pin UnoPin17; +typedef fglcd::Pin UnoPin18; +typedef fglcd::Pin UnoPin19; +typedef fglcd::TM1638 Panel; + +#define CFG_SCRATCH_MAX_AREAS 2 // 0/undef to disable scratch +#define CFG_SCRATCH_BLOCK_SIZE 256 +#define CFG_SCRATCH_ALIGNMENT 256 + +#define CFG_PAL_SIZE 128 +#define CFG_PAL_START_LO (scratch0) +#define CFG_PAL_START_HI (&scratch0[128]) // must be directly located behind CFG_PAL_START_LO in memory + +#define CFG_PTR_ISINTAB (scratch1) +#define CFG_PTR_USINTAB (scratch1) + +#define CFG_PTR_DECOMPWINDOW (scratch1) + +#define CFG_MAX_EVENTS 4 // 0/undef to disable event system +#define CFG_USE_HIGH_PRECISION_EVENT_TIMER 0 + +*/ + diff --git a/democode/src/demolib/comp_rle.cpp b/democode/src/demolib/comp_rle.cpp new file mode 100644 index 0000000..571dee7 --- /dev/null +++ b/democode/src/demolib/comp_rle.cpp @@ -0,0 +1,219 @@ +// RLE compressor that takes byte-wise input +// starts equal runs on the 3rd consecutive equal char +// output may be different from the RLE compressor in the tools (rle.h) +// but does not need the complete input up-front + +// FIXME TEST THIS + +#include "comp_rle.h" + +enum RLEState +{ + UNDECIDED, + COPY, + FILL +}; + +enum RLEOpMask +{ + M_COPY = 0x80, + M_FILL = 0 +}; + +enum RLELimits +{ + RLEMAX = 127 // inclusive +}; + +RLECompState::RLECompState(void * dst) + : wptr((uint8_t*)dst) + , ctrlp(NULL) + , offs(0) + , lastbyte(0) + , ctr(0) + , base((uint8_t*)dst) +{ +} + +static FORCEINLINE void flushfill(RLECompState *rs, uint8_t b) +{ + // must write control byte and rep byte + *rs->wptr++ = b; + *rs->ctrlp = M_FILL | rs->ctr; +#ifdef _DEBUG + rs->ctrlp = NULL; +#endif +} + +static FORCEINLINE void flushcopy(RLECompState *rs, uint8_t len) +{ + // bytes were already written, but control byte must be put + *rs->ctrlp = M_COPY | len; +#ifdef _DEBUG + rs->ctrlp = NULL; +#endif +} + +static FORCEINLINE uint8_t copylen(RLECompState *rs) +{ + return uint8_t(rs->wptr - rs->ctrlp) - 1; +} + + +void RLECompState::addByte(uint8_t b) +{ + switch(state) + { + case FILL: + if(b == lastbyte && ctr < RLEMAX) + ++ctr; + else + { + flushfill(this, b); + goto startfresh; + } + break; + + case COPY: // in this mode, ctr counts repetitions (consecutive eq. bytes) + { + // can we fit another byte? if not, finish the run and restart + const uint8_t len = copylen(this); + if(len >= RLEMAX) + { + flushcopy(this, len); + goto startfresh; + } + *wptr++ = b; + if(b != lastbyte) + { + lastbyte = b; + ctr = 0; + } + else + { + if(ctr == 2) + { + // 3rd equal byte, finish copy run and start a fill run + wptr -= 2; // 2 equal bytes already written + flushcopy(this, len); + goto startfill; + } + ++ctr; + } + } + break; + + case UNDECIDED: + switch(ctr) + { + startfresh: // yeah i know. labels in switches, yolo + state = UNDECIDED; + ctr = 0; + case 0: // first byte after a run, nothing to do at this point + lastbyte = b; + break; + case 1: // 2nd byte, mismatch opens a copy run + if(b != lastbyte) + goto mismatch; + break; // nothing to do + case 2: // 3rd byte in a sequence, another match opens a fill run + if(b == lastbyte) + { + startfill: + state = FILL; + uint8_t *p = wptr; + ctrlp = p++; + // ctr is already 2 here + } + else // two equal bytes, but 3rd byte is different + { + mismatch: + uint8_t *p = wptr; + ctrlp = p++; // will write the size to here +#ifdef _DEBUG + *ctrlp = 0xff; +#endif + // flush 1 or 2 buffered, equal bytes + { + uint8_t prev = lastbyte; + uint8_t todo = ctr; // 1 or 2, never 0 + FGLCD_ASSERT(todo >= 1 && todo <= 2, "rleoops"); + do + *p++ = prev; + while(--todo); + } + + *p++ = b; // this is the mismatched byte + wptr = p; + + ctr = 0; + state = COPY; + } + break; + } + ++ctr; + break; + + } +} + +void RLECompState::addByteRep(uint8_t b, uint16_t n) +{ + // first up to 3 bytes will bring it into the right state cleanly + // must always push one byte even if we're already in FILL mode, since + // the last char might be different from what we have now. + do + { + if(!n) + return; + --n; + addByte(b); + } + while(state != FILL); + + // now we know: state == FILL, lastbyte == b + // unknown: ctr = ?? + + // from here we can go in big blocks + uint32_t N = uint32_t(n) + ctr; // maybe there's something in the pipeline already + uint8_t *p = wptr, *c = ctrlp; + while(N > RLEMAX) // emit full runs (2 bytes each) + { + *c = RLEMAX; + *p++ = b; + c = p++; + N -= RLEMAX; + } + // remaining incomplete run + // (if exactly RLEMAX, run will be completed on next append (or flush)) + wptr = p; + ctrlp = c; + ctr = N; // meaning is the same for both modes below + state = N <= 2 ? UNDECIDED : FILL; + //lastbyte = b; // already known +} + +unsigned RLECompState::flush() +{ + switch(state) + { + case UNDECIDED: // got 1 byte or 2 equal bytes -> make this a fill run, too + FGLCD_ASSERT(ctr < 2, "rlepflu"); + // fall through + case FILL: // 3+ bytes + flushfill(this, lastbyte); + break; + + case COPY: + flushcopy(this, copylen(this)); + break; + } + + ctr = 0; + state = UNDECIDED; + + const unsigned oldoffs = offs; + *wptr++ = 0; + offs = wptr - base; + return oldoffs; +} diff --git a/democode/src/demolib/comp_rle.h b/democode/src/demolib/comp_rle.h new file mode 100644 index 0000000..2992aa2 --- /dev/null +++ b/democode/src/demolib/comp_rle.h @@ -0,0 +1,32 @@ +#pragma once + +#include "decomp.h" + + +struct RLECompState +{ + RLECompState(void *dst); + void addByte(uint8_t b); + void addByteRep(uint8_t b, uint16_t n); + unsigned flush(); // returns offset at which the run starts + + uint8_t *wptr, *ctrlp; + unsigned offs; + uint8_t lastbyte; + uint8_t state; + uint8_t ctr; // universal counter; different meaning per state + uint8_t * const base; +}; + +// adapter for dynamic recompression template wizardry +struct DecompToRLE +{ + RLECompState state; + + DecompToRLE(void *dst) : state(dst) {} + FORCEINLINE void emitSingleByte(uint8_t c) { state.addByte(c); } + FORCEINLINE void emitMultiByte(uint8_t c, uint16_t len) { state.addByteRep(c, len); } +}; + +template<> struct SelectSideEffect { typedef DecompToRLE type; }; + diff --git a/democode/src/demolib/debugthing.cpp b/democode/src/demolib/debugthing.cpp new file mode 100644 index 0000000..b7cbfcf --- /dev/null +++ b/democode/src/demolib/debugthing.cpp @@ -0,0 +1,131 @@ +#include "debugthing.h" +#include "eventsystem.h" +#include "fglcd/chip_tm1638.h" +#include "../demomath/fgmath.h" +#include "cfg-demo.h" +#include "globals.h" +#include "demodebug.h" + +#include + +uint8_t DebugThing::NumInstances() +{ +#ifdef CFG_USE_DEBUG_THING + return G.debug.numDebugInstances; +#endif + return 0; +} + +void DebugThing::Event(void *self) +{ + DebugThing *th = (DebugThing*)self; + if(th->_done) + th->_done = 2; + else + { + const uint8_t b = Panel::getButtons(); + const uint8_t bx = b ^ th->_btn; + if(th->_func) + th->_func(*th, th->_ud, b, bx); + th->_btn = b; + th->schedule(); + } +} + +void DebugThing::schedule() +{ + evs::schedule(50, Event, this); +} + +void DebugThing::StaticInit() +{ + Panel::init(); + Panel::intensity(2); +} + +DebugThing::DebugThing(Func f, void *ud) + : DebugThing(f, ud, noinit) +{ +#ifdef CFG_USE_DEBUG_THING + G.debug.numDebugInstances++; +#endif + init(); +} + +#ifdef CFG_USE_DEBUG_THING +DebugThing::DebugThing() // default init is used for the global debugger + : DebugThing(dbg_demomain, &G.debug.vars, DebugThing::noinit) +{} + +DebugThing::DebugThing(Func f, void *ud, NoInit) + : _func(f), _ud(ud), _done(0) +{ +} +#endif + +void DebugThing::init() +{ + //Panel::clear(); + //Panel::setText(PSTR("DEBUG")); + _btn = Panel::getButtons(); // important to get the initial button configuration. (buttons are all 0xff if no panel is attached!) + schedule(); +} + +DebugThing::~DebugThing() +{ + _done = 1; + while(_done != 2) {} + Panel::clear(); +#ifdef CFG_USE_DEBUG_THING + G.debug.numDebugInstances--; +#endif +} + +void DebugThing::setNum(int n, uint8_t beg) +{ +#ifdef CFG_USE_DEBUG_THING + char buf[10]; + snprintf_P(&buf[0], sizeof(buf), PSTR("%8d "), n); + setTextRAM(&buf[0], beg); +#endif +} + +void DebugThing::set2Num(int x, int y) +{ +#ifdef CFG_USE_DEBUG_THING + x = vclamp(x, -999, 9999); + y = vclamp(y, -999, 9999); + char buf[9]; + snprintf_P(&buf[0], sizeof(buf), PSTR("%4d%4d"), x, y); + setTextRAM(&buf[0]); +#endif +} + +void DebugThing::set2NumHex(unsigned x, unsigned y) +{ +#ifdef CFG_USE_DEBUG_THING + char buf[9]; + snprintf_P(&buf[0], sizeof(buf), PSTR("%4X%4X"), x, y); + setTextRAM(&buf[0]); +#endif +} + +void DebugThing::setLEDs(uint8_t mask) +{ + Panel::setLEDs(mask); +} + + +void DebugThing::setTextRAM(const char * s, uint8_t beg) +{ + Panel::setText(s, beg); +} +void DebugThing::setRaw(uint8_t pos, uint8_t val) +{ + Panel::setRaw(pos, val); +} + +void DebugThing::commit() +{ + Panel::commit(); +} diff --git a/democode/src/demolib/debugthing.h b/democode/src/demolib/debugthing.h new file mode 100644 index 0000000..041d0d9 --- /dev/null +++ b/democode/src/demolib/debugthing.h @@ -0,0 +1,43 @@ +#pragma once + +#include "cfg-demo.h" +#include + +class DebugThing +{ +public: + enum NoInit { noinit }; + typedef void (*Func)(DebugThing&, void*, uint8_t buttons, uint8_t changed); + + void init(); + ~DebugThing(); + inline uint8_t getButtons() const { return _btn; } + + static void setTextRAM(const char *s, uint8_t beg = 0); + static void setNum(int n, uint8_t beg = 0); + static void set2Num(int x, int y); + static void set2NumHex(unsigned x, unsigned y); + static void setLEDs(uint8_t mask); + static void setRaw(uint8_t pos, uint8_t val); + static void commit(); + + static FORCEINLINE uint8_t Alone() { return NumInstances() == 0; } + +#ifndef CFG_USE_DEBUG_THING +private: // just disallow construction, is the easiest thing to prevent accidental instantiation +#endif + DebugThing(); + DebugThing(Func, void*); + DebugThing(Func, void*, NoInit); + static void StaticInit(); + +private: + static void Event(void *self); + void schedule(); + Func const _func; + void * const _ud; + uint8_t _btn; + volatile uint8_t _done; + static uint8_t NumInstances(); // instances created with noinit flag are considered background and not counted +}; + diff --git a/democode/src/demolib/decomp.h b/democode/src/demolib/decomp.h new file mode 100644 index 0000000..facabed --- /dev/null +++ b/democode/src/demolib/decomp.h @@ -0,0 +1,142 @@ +#pragma once + +#include "fglcd/mcu.h" +#include "../demomath/traits.h" + +#include "decomp_rle.h" +#include "decomp_tschunk.h" +#include "decomp_exo.h" +#include "decomp_mix.h" + + +template +struct DecompState +{ + const typename Reader::Pointer _src; + const unsigned _size; + FORCEINLINE DecompState(typename Reader::Pointer src, unsigned size) + : _src(src), _size(size) + {} + + template + FORCEINLINE void decomp(void *dst) + { + typedef typename SelectWindowType::type Window; + Window w(dst); + decomp(w); + } + + template + FORCEINLINE void decomp(Window& w) + { + w.emitLiterals(_src, _size); + } +}; + +template +struct DecompState +{ + const typename Reader::Pointer _src; + FORCEINLINE DecompState(typename Reader::Pointer src, unsigned) + : _src(src) + {} + + template + FORCEINLINE void decomp(void *dst) + { + typedef typename SelectWindowType::type Window; + Window w(dst); + decomp(w); + } + + template + FORCEINLINE void decomp(Window& w) + { + rledecomp(w, _src); + } +}; + +template +struct DecompState +{ +private: + typename Reader::Pointer _src; + ExoState _exo; +public: + FORCEINLINE DecompState(typename Reader::Pointer src, unsigned) + { + _src = exo_decrunch_init(_exo, src); + } + + template + FORCEINLINE void decomp(void *dst) + { + typedef typename SelectWindowType::type Window; + Window w(dst); + decomp(w); + } + + template + FORCEINLINE void decomp(Window& w) + { + exo_decrunch(_exo, _src, w); + } +}; + +template +struct DecompState +{ + const typename Reader::Pointer _src; + const unsigned _ncmd; + FORCEINLINE DecompState(typename Reader::Pointer src, unsigned) + : _src(src + 2), _ncmd(Reader::template read(src)) + { + } + + template + FORCEINLINE void decomp(void *dst) + { + typedef typename SelectWindowType::type Window; + Window w(dst); + decomp(w); + } + + template + FORCEINLINE void decomp(Window& w) + { + tschunkdecomp(w, _src, _ncmd); + } +}; + + +template +FORCEINLINE void decompressRAM(void *dst, typename Reader::Pointer src, unsigned sz) +{ + DecompState dec(src, sz); + dec.template decomp(dst); +} + +template +struct _DecompDataStorage +{ + uint8_t unpacked[TImage::fullsize + extrasize]; + typedef fglcd::RAM Mem; + static const unsigned windowsize = unsigned(TImage::windowsize < TImage::fullsize ? TImage::windowsize : TImage::fullsize); + + void _prepare(unsigned decompToOffs = 0) + { + decompressRAM(&unpacked[decompToOffs], fglcd_get_farptr(TImage::data), TImage::packedsize); + } + + FORCEINLINE uint8_t *ptr() { return &unpacked[0]; } + FORCEINLINE const uint8_t *ptr() const { return &unpacked[0]; } +}; + +// If the image is already uncompressed in ROM, no need to copy anything +template +struct _DecompDataStorage +{ + typedef fglcd::ProgmemFar Mem; + FORCEINLINE static fglcd::FarPtr ptr() { return fglcd_get_farptr(TImage::data); } + FORCEINLINE void _prepare() {} +}; diff --git a/democode/src/demolib/decomp_def.h b/democode/src/demolib/decomp_def.h new file mode 100644 index 0000000..7233e67 --- /dev/null +++ b/democode/src/demolib/decomp_def.h @@ -0,0 +1,22 @@ +#pragma once + +#include "packdef.h" + +enum DecompWindowType +{ + DECOMP_WINDOW_NONE, + DECOMP_WINDOW_OUTPUT, // use output buffer as window + DECOMP_WINDOW_BIG, // use internal buffer as 16bit addressable window (but handle wraparound) + DECOMP_WINDOW_TINY, // use internal 8-bit addressable buffer window (faster, implicit wraparound) +}; + +enum DecompDst +{ + ToRAM, + ToLCD, + ToRLE, // recompress as RLE +}; + +// these are specialized as needed +template struct SelectSideEffect; +template struct DecompState; diff --git a/democode/src/demolib/decomp_exo.h b/democode/src/demolib/decomp_exo.h new file mode 100644 index 0000000..5e37413 --- /dev/null +++ b/democode/src/demolib/decomp_exo.h @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2005 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented * you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + */ + +// Hacked to support Exomizer 3.0 and C++-ified -- fg +// For mode -P15 + +#pragma once + +#include "decomp_window.h" +#include "demo_def.h" +#include "assert.h" + +struct ExoState +{ + uint16_t base[52]; + uint8_t bits[52]; +}; + +template struct TypeForBits { typedef typename TypeForBits::type type; }; +template<> struct TypeForBits<8> { typedef uint8_t type; }; +template<> struct TypeForBits<16> { typedef uint16_t type; }; + +// For some reason this decompresses garbage only +// But no time to fix it, i can see the deadline incoming aaaaargh +//#define READBYTE(x) (Reader::readIncSafe(x)) + +// This works... +#define READBYTE(x) (Reader::template read(x++)) + +static FORCEINLINE uint8_t +bitbuffer_rotate(uint8_t *bb, uint8_t carry) // ROL +{ + const uint8_t carry_out = *bb >> 7u; + *bb <<= 1; + *bb |= carry; + return carry_out; +} + + +template +static FORCEINLINE uint16_t +read_bits(uint8_t *bit_buffer, typename Reader::Pointer& inp, uint8_t bit_count) +{ + uint16_t bits = 0; + const uint8_t byte_copy = bit_count & 8; + bit_count &= 7; + + uint8_t bb = *bit_buffer; + while(bit_count--) + { + uint8_t carry = bitbuffer_rotate(&bb, 0); + if (bb == 0) + { + //FGLCD_ASSERT(carry == 1); + bb = READBYTE(inp); + carry = bitbuffer_rotate(&bb, 1); + } + bits <<= 1; + bits |= carry; + } + *bit_buffer = bb; + + if(byte_copy) + { + bits <<= 8; + bits |= READBYTE(inp); + } + return bits; +} + +template +static FORCEINLINE typename TypeForBits::type +read_bits(uint8_t *bit_buffer, typename Reader::Pointer& inp) +{ + typedef typename TypeForBits::type R; + return (R) read_bits(bit_buffer, inp, bit_count); +} + +// optional; for speed +template +static NOINLINE uint8_t +read_bits(uint8_t *bit_buffer, typename Reader::Pointer& inp) +{ + uint8_t bb = *bit_buffer; + uint8_t carry = bitbuffer_rotate(bb, 0); + if (bb == 0) + { + assert(carry == 1); + bb = READBYTE(inp); + carry = bitbuffer_rotate(bb, 1); + } + *bit_buffer = bb; + return carry; +} + + + + +template +static void +init_table(ExoState& exo, typename Reader::Pointer& inp) +{ + uint8_t bit_buffer = 0x80; + uint16_t b2; + for(uint8_t i = 0; i < 52; ++i) + { + if((i & 15) == 0) + b2 = 1; + exo.base[i] = b2; + + uint8_t b1 = read_bits(&bit_buffer, inp); + b1 |= read_bits(&bit_buffer, inp) << 3; + exo.bits[i] = b1; + + b2 += 1u << b1; + } +} + +template +static NOINLINE uint16_t +read_length(const ExoState& exo, uint8_t *bit_buffer, typename Reader::Pointer& in, uint8_t index) +{ + return exo.base[index] + read_bits(bit_buffer, in, exo.bits[index]); +} + +template +static NOINLINE uint8_t +read_gamma(uint8_t *bit_buffer, typename Reader::Pointer& in) +{ + uint8_t g = 0; + while(!read_bits(bit_buffer, in)) + ++g; + return g; +} + +template +typename Reader::Pointer +exo_decrunch_init(ExoState& exo, typename Reader::Pointer in) +{ + typename Reader::SegmentBackup seg; + init_table(exo, in); + return in; +} + +template +void +exo_decrunch(const ExoState& exo, typename Reader::Pointer in, Emit& out) +{ + typename Reader::SegmentBackup seg; + uint8_t bit_buffer = 0x80; + goto implicit_literal_byte; + + for(;;) + { + if(read_bits(&bit_buffer, in)) // literal? + { + implicit_literal_byte: + const uint8_t c = READBYTE(in); + out.emitSingleByte(c); + continue; + } + uint8_t index = read_gamma(&bit_buffer, in); + if(index == 16) // stop code? + break; + if(index == 17) + { + const uint8_t hi = READBYTE(in); + const uint8_t lo = READBYTE(in); + const uint16_t length = (hi << 8u) | lo; + out.template emitLiterals(in, length); // TODO: make fast yo + continue; + } + const uint16_t length = read_length(exo, &bit_buffer, in, index); + + index = (length < 3 ? length : 3) - 1u; + index = (48u - 16u*index) + read_bits(&bit_buffer, in, index ? 4 : 2); + + const uint16_t offset = read_length(exo, &bit_buffer, in, index); + out.emitOffset(offset, length); + } +} + +template +void +exo_decrunch_single(typename Reader::Pointer in, Emit& out) +{ + typename Reader::SegmentBackup seg; + ExoState exo; + in = exo_decrunch_init(exo, in); + exo_decrunch(exo, in, out); +} + +#undef READBYTE + + +template +struct DecompState +{ + typename Reader::Pointer _data, _offsets; + + ExoState exo; + + DecompState(typename Reader::Pointer src, unsigned) + { + src = exo_decrunch_init(exo, src); + + const uint16_t N = Reader::template read(src); src += 2; + const uint16_t skip = 2u * N; + _offsets = src; src += skip; + _data = src; + } + + template + void decompBlock(void *dst, unsigned block) const + { + FGLCD_ASSERT(_offsets + 2*block < _data, "exodcblk"); + typedef typename SelectWindowType::type Window; + Window w(dst); + const uint16_t offs = Reader::template read(_offsets, block); + const typename Reader::Pointer p = _data + offs; + + exo_decrunch(exo, p, w); + } +}; diff --git a/democode/src/demolib/decomp_lcd.h b/democode/src/demolib/decomp_lcd.h new file mode 100644 index 0000000..6e17f7f --- /dev/null +++ b/democode/src/demolib/decomp_lcd.h @@ -0,0 +1,178 @@ +#pragma once + +#include "decomp.h" +#include "drawhelper.h" +#include "cfg-demo.h" + +template // emit pixels to LCD given a previously set palette +struct DecompToLCD +{ + typedef typename TheLCD::ColorType Color; + static FORCEINLINE void emitSingleByte(uint8_t c) { TheLCD::sendPixel(palgetcolor_inl(c)); } + static FORCEINLINE void emitMultiByte(uint8_t c, uint16_t len) { TheLCD::setColor(palgetcolor_inl(c)); TheLCD::fastfill_u16(len); } + //static FORCEINLINE void emitMultiByte(uint8_t c, uint8_t len) { TheLCD::setColor(palgetcolor_inl(c)); TheLCD::fastfill_u8(len); } +}; + +template<> struct SelectSideEffect { typedef DecompToLCD type; }; + + +// -- Decomp entire image to RAM/LCD --- + + +// Unpack image to RAM +template +struct DecompImageData : public _DecompDataStorage, public TImage +{ + static_assert(!TImage::blocked, "image should be a single large block"); + typedef _DecompDataStorage Storage; + typedef typename Storage::Mem Mem; + FORCEINLINE DecompImageData() + { + this->_prepare(); + } + FORCEINLINE void applypal(uint8_t offs = 0) + { + applyPal16_Image(offs); + } +}; + +template +FORCEINLINE void decompressToLCD(void *dst, typename Reader::Pointer src, unsigned sz) +{ + DecompState dec(src, sz); + dec.template decomp(dst); +} + +template +struct DrawImageHelper +{ + static const unsigned windowsize = unsigned(TImage::windowsize < TImage::fullsize ? TImage::windowsize : TImage::fullsize); + static const unsigned w = TImage::w; + static const unsigned h = TImage::h; + + FORCEINLINE void applypal() + { + applyPal16_Image(); + } + + FORCEINLINE void draw(unsigned x, unsigned y) + { + LCD::setxywh(x, y, TImage::w, TImage::h); + decompressToLCD(NULL, fglcd_get_farptr(TImage::data), TImage::packedsize); + } +}; + + + +// -- Decomp image blocks to RAM/LCD --- + +template +struct _DecompImageBlocksHelper +{ + static const unsigned totalblocks = 1; +}; + +template +struct _DecompImageBlocksHelper +{ + static const unsigned totalblocks = unsigned((uint32_t(TImage::w) * TImage::h) / (uint32_t(TImage::blockw) * TImage::blockh)); +}; + +template +struct DecompImageBlocks : public TImage +{ + static_assert(TImage::blocked, "image should consist of multiple blocks"); + + static const unsigned _windowsize = unsigned(TImage::windowsize < TImage::fullsize ? TImage::windowsize : TImage::fullsize); + typedef DecompState _Decomp; + + static const unsigned totalblocks = _DecompImageBlocksHelper::totalblocks; + + const _Decomp dec; + + FORCEINLINE DecompImageBlocks() + : dec(fglcd_get_farptr(TImage::data), TImage::packedsize) + { + } + + template + FORCEINLINE void unpackBlock(void *dst, unsigned block) + { + FGLCD_ASSERT_VAL2(block < totalblocks, "unpkblk", block, totalblocks); + dec.template decompBlock(dst, block); + } + + FORCEINLINE void toRAM(void *dst, unsigned block) + { + unpackBlock(dst, block); + } + + FORCEINLINE void toLCD(unsigned block) + { + unpackBlock(NULL, block); + } + + void applypal(uint8_t offs = 0) + { + applyPal16_Image(offs); + } +}; + +// bit of a hack to load blocks from 2 images with differing palettes +// layout: [ ImageA | ImageB ] +template +struct DecompStitchedImageBlocks +{ + static const unsigned w = TImageA::w + TImageB::w; + static const unsigned h = TImageA::h; + //static_assert(h == TImageB::h); + + DecompImageBlocks _decA; + DecompImageBlocks _decB; + + uint8_t _lastid; + + DecompStitchedImageBlocks() + : _lastid(0xff) + { + } + + FORCEINLINE void applypal(uint8_t = 0) {} + + void _prep(uint8_t id) + { + if(id != _lastid) + { + _lastid = id; + if(!id) + _decA.applypal(0); + else + _decB.applypal(0); + } + } + + template + FORCEINLINE void unpackBlock(void *dst, unsigned block) + { + if(block < _decA.totalblocks) + { + _prep(0); + _decA.template unpackBlock
(dst, block); + } + else + { + _prep(1); + _decB.template unpackBlock
(dst, block - _decA.totalblocks); + } + } + + FORCEINLINE void toRAM(void *dst, unsigned block) + { + unpackBlock(dst, block); + } + + FORCEINLINE void toLCD(unsigned block) + { + unpackBlock(NULL, block); + } +}; diff --git a/democode/src/demolib/decomp_mix.h b/democode/src/demolib/decomp_mix.h new file mode 100644 index 0000000..10f0ed5 --- /dev/null +++ b/democode/src/demolib/decomp_mix.h @@ -0,0 +1,127 @@ +#pragma once + +#include "decomp_window.h" + +template +struct DecompState +{ + typename Reader::Pointer _data, _offsets, _ncmd; + ExoState _exo; + DecompState(typename Reader::Pointer src, unsigned) + { + typename Reader::SegmentBackup seg; + const uint8_t usedbits = Reader::readIncSafe(src); + if(usedbits & (1 << PK_EXO)) + src = exo_decrunch_init(_exo, src); + if(usedbits & (1 << PK_TSCHUNK)) + { + uint16_t N = Reader::template read(src); src += 2; + _ncmd = src; + src += 2u * N; + } + uint16_t noffs = Reader::template read(src); src += 2; + _offsets = src; + src += 2u * noffs; + + _data = src; + } + + struct Hdr + { + uint8_t algo; + uint16_t num; + }; + + FORCEINLINE Hdr gethdr(typename Reader::Pointer& p) const + { + uint8_t b = Reader::readIncSafe(p); + const uint8_t large = b & 1; + b >>= 1u; + uint8_t n = 0; + uint8_t bit; + do + { + bit = b & 1; + b >>= 1u; + n += bit; + } + while(bit); + + Hdr h; + h.algo = n; + h.num = large ? fglcd::make16(Reader::readIncSafe(p), b) : b; + return h; + } + + template + void decompBlock(void *dst, unsigned block) const + { + typename Reader::SegmentBackup seg; + typename Reader::Pointer p = _data + Reader::template read(_offsets, block); + const Hdr h = gethdr(p); + switch(h.algo) + { + case PK_UNCOMP: + { + typedef typename SelectWindowType::type Window; + Window w(dst); + w.template emitLiterals(p, h.num); + break; + } + case PK_RLE: + { + typedef typename SelectWindowType::type Window; + Window w(dst); + rledecomp(w, p); + break; + } + case PK_EXO: + { + typedef typename SelectWindowType::type Window; + Window w(dst); + exo_decrunch(_exo, p, w); + break; + } + case PK_TSCHUNK: + { + typedef typename SelectWindowType::type Window; + Window w(dst); + uint16_t ncmd = Reader::template read(_ncmd, h.num); + tschunkdecomp(w, p, ncmd); + break; + } + } + } +}; + + +template +struct DecompState +{ + typename Reader::Pointer _data, _offsets, _ncmd; + uint8_t _big; + DecompState(typename Reader::Pointer src, unsigned) + { + typename Reader::SegmentBackup seg; + const uint16_t N = Reader::template read(src); src += 2; + const uint8_t big = Reader::readIncSafe(src); + _big = big; + const uint16_t skip = 2u * N; + _ncmd = src; src += big ? skip : N; + _offsets = src; src += skip; + _data = src; + } + + template + void decompBlock(void *dst, unsigned block) const + { + typename Reader::SegmentBackup seg; + FGLCD_ASSERT(_offsets + 2*block < _data, "tscdcblk"); + typedef typename SelectWindowType::type Window; + Window w(dst); + const uint16_t offs = Reader::template read(_offsets, block); + const uint16_t ncmd = _big ? Reader::template read(_ncmd, block) : Reader::template read(_ncmd, block); + const typename Reader::Pointer p = _data + offs; + tschunkdecomp(w, p, ncmd); + } +}; diff --git a/democode/src/demolib/decomp_rle.h b/democode/src/demolib/decomp_rle.h new file mode 100644 index 0000000..ab4d36f --- /dev/null +++ b/democode/src/demolib/decomp_rle.h @@ -0,0 +1,115 @@ +#pragma once + +#include "decomp_window.h" +#include "../demomath/fgmath.h" + +template +void rledecomp(Emit& out, typename Reader::Pointer src) +{ + typename Reader::SegmentBackup seg; + + while(unsigned char c = Reader::readIncSafe(src)) + { + if(c & 0x80) // copy + { + c &= ~0x80; + out.template emitLiterals(src, c); + src += c; + } + else // fill + { + const unsigned char v = Reader::readIncSafe(src); + out.emitMultiByte(v, c); + } + } +} + +template +struct DecompState +{ + typename Reader::Pointer _data, _offsets; + + DecompState(typename Reader::Pointer src, unsigned) + { + const uint16_t N = Reader::template read(src); src += 2; + const uint16_t skip = 2u * N; + _offsets = src; src += skip; + _data = src; + } + + template + void decompBlock(void *dst, unsigned block) const + { + FGLCD_ASSERT(_offsets + 2*block < _data, "rledcblk"); + typedef typename SelectWindowType::type Window; + Window w(dst); + const uint16_t offs = Reader::template read(_offsets, block); + const typename Reader::Pointer p = _data + offs; + + rledecomp(w, p); + } +}; + + +// NOT YET USED +template +FORCEINLINE void srledecomp_inner(Emit& out, typename Reader::Pointer& src, typename Reader::Pointer& valptr, unsigned num, uint8_t stride) +{ + typename Reader::SegmentBackup seg; + + // valptr and src are both located in the same memory type. + // But we need to read from two distinct pointers. And the only pointer that can read from PROGMEM is Z. + // Usually that would require changing Z twice per value: + // The first time to read the value (single byte), the second time to read the count (another single byte) + // In order to prevent Z ping-pong, copy the values data into scratch space so we can use X or Y for the values ptr. + // This speeds up processing a lot since auto-incrementing reads can be used, and AVR's annoying 24-bit addressing is less of a hassle. + uint8_t * const lut = CFG_PTR_DECOMPWINDOW; + + while(num) + { + // copy values LUT to RAM, packed + const uint8_t n = (uint8_t)vmin(num, 255); + { + if(stride == 1) + { + Reader::Memcpy(lut, valptr, n); + valptr += n; + } + else // strided copy, can't autoinc anymore and need to use the slower copy code + { + uint8_t *dst = lut; + uint8_t nn = n; + do + { + const uint8_t x = Reader::template read(valptr); + fglcd::RAM::storeIncFast(dst, x); + valptr += stride; + } + while(--nn); + } + } + + // main copy loop + //Reader::SetSegmentPtr(src); + uint8_t nn = n; + const uint8_t * lutrd = lut; + do + { + const uint8_t val = fglcd::RAM::readIncSafe(lutrd); // could be replaced by {val = *valptr; valptr += stride;} with the above block omitted if this wasn't supposed to be fast on AVR + if(uint16_t rep = decomp::readN(src)) + out.emitMultiByte(val, rep); + } + while(--nn); // 256 times if begin with nn == 0 + //Reader::fixFarPtr(src); + num -= n; + } +} + +template +void srledecomp(Emit& out, typename Reader::Pointer src, unsigned offset, unsigned num, uint8_t stride) +{ + typename Reader::Pointer valptr = src - offset; + if(!offset) + src += num; + srledecomp_inner(out, src, valptr, num, stride); +} diff --git a/democode/src/demolib/decomp_tschunk.h b/democode/src/demolib/decomp_tschunk.h new file mode 100644 index 0000000..6833b17 --- /dev/null +++ b/democode/src/demolib/decomp_tschunk.h @@ -0,0 +1,78 @@ +#pragma once + +#include "decomp_util.h" +#include "decomp_rle.h" + +namespace tschunk { + +template +NOINLINE static void _decompress2(Emitter& em, typename Mem::Pointer p, unsigned ncmd) +{ + do + { + const uint8_t ctrl = Mem::readIncSafe(p); + uint16_t n = decomp::readNRaw(ctrl, p); + const uint8_t cmd = ctrl & 0xc0; + if(!cmd) // 00: rep lit byte (n) + { + const uint8_t val = Mem::readIncSafe(p); + em.emitMultiByte(val, n); + } + else if(cmd == 0x40) // 01: copy lit bytes (n) + { + if(n) // copy next n bytes + { + em.template emitLiterals(p, n); + } + else // skip next n bytes + { + n = decomp::readN(p); + } + p += n; + } + else + { + uint8_t rep = 1; + if(n & 1) + rep += Mem::readIncSafe(p); // += 0xff wraps to 0x00, which is 0xff+1 times effectively + + n >>= 1u; + typename Mem::Pointer pin = p; + pin -= decomp::readN(p); // offset backwards + + if(cmd == 0x80) // 10: copy pgm - offs (n) + { + do // always once, but rep more + em.template emitLiterals(pin, n); + while(--rep); + } + else // 11: exec pgm - offs (n) + { + typename Mem::SegmentBackup seg; + FGLCD_ASSERT(n, "tscexec0"); + /*if(!n) // special case: use strided-RLE variant + { + n = decomp::readN(p); + srledecomp_inner(em, p, pin, n, rep); // use rep as stride (default case of 1 is very fast) + } + else // recursively call into self at offset + */ do + _decompress2(em, pin, n); + while(--rep); + } + } + } + while(--ncmd); +} + +} // end namespace tschunk + +template +FORCEINLINE void tschunkdecomp(Emitter& em, typename Mem::Pointer p, uint16_t ncmd) +{ + if(ncmd) + { + typename Mem::SegmentBackup seg; + tschunk::_decompress2(em, p, ncmd); + } +} diff --git a/democode/src/demolib/decomp_util.h b/democode/src/demolib/decomp_util.h new file mode 100644 index 0000000..4868509 --- /dev/null +++ b/democode/src/demolib/decomp_util.h @@ -0,0 +1,39 @@ +#pragma once + +#include "fglcd/mcu.h" +#include "demo_def.h" +#include "scratch.h" + +namespace decomp { + +template +static FORCEINLINE uint16_t readNRaw(uint8_t ctrl, typename Mem::Pointer& p) +{ + static const uint8_t MORE = (1 << bit); + static const uint8_t MASK = MORE - 1; + uint8_t n = ctrl & MASK; + if(!(ctrl & MORE)) + return n; // highest bit is never set, so this can't overflow + + const uint8_t lo = Mem::readIncSafe(p); + return fglcd::make16(lo, n); +} + +template +static FORCEINLINE uint16_t readN(typename Mem::Pointer& p) +{ + uint16_t ret; + const uint8_t a = Mem::readIncSafe(p); + if(!(a & 0x80)) + ret = a; + else + { + const uint8_t b = Mem::readIncSafe(p); + ret = fglcd::make16(b, a & 0x7f); + } + return ret; +} + + + +} // end namespace decomp diff --git a/democode/src/demolib/decomp_window.h b/democode/src/demolib/decomp_window.h new file mode 100644 index 0000000..a14a9f3 --- /dev/null +++ b/democode/src/demolib/decomp_window.h @@ -0,0 +1,260 @@ +#pragma once + +#include "decomp_def.h" +#include "decomp_util.h" + +#include "scratch.h" + + +// "Side-effect" when emitting bytes + +struct DecompToRAM // no extra side effect +{ + static FORCEINLINE void emitSingleByte(uint8_t c) {} + static FORCEINLINE void emitMultiByte(uint8_t c, unsigned len) {} +}; + +template<> struct SelectSideEffect { typedef DecompToRAM type; }; + + + +// Class to handle decompressor window + +template +struct DecompEmitterWindow; + +template +struct DecompEmitterWindow // No window -> suitable for RLE or RRR +{ + struct Action + { + FORCEINLINE Action(const SideEffect& eff_) : eff(eff_) {} + FORCEINLINE void operator()(uint8_t x) { eff.emitSingleByte(x); } + SideEffect eff; + }; + Action _a; + FORCEINLINE DecompEmitterWindow(void * = NULL, unsigned = 0, const SideEffect& eff = SideEffect()) : _a(eff) {} + FORCEINLINE void emitSingleByte(uint8_t c) { _a(c); } + FORCEINLINE void emitMultiByte(uint8_t c, unsigned len) { _a.eff.emitMultiByte(c, len); } + + template + FORCEINLINE void emitLiterals(typename Reader::Pointer rd, unsigned len) + { + //FGLCD_ASSERT((uintptr_t)rd < (uintptr_t)rd + len, "gotcha"); + Reader::ForRange_inl(rd, len, _a); + } + //void emitOffset(unsigned offset, unsigned len); // does not exist without a window +}; + +template +struct DecompEmitterWindow +{ + struct Action + { + FORCEINLINE Action(const SideEffect& eff_, uint8_t *p_) : p(p_), eff(eff_) {} + FORCEINLINE void operator()(uint8_t x) { fglcd::RAM::storeIncFast(p, x); eff.emitSingleByte(x); } + uint8_t *p; + SideEffect eff; + }; + Action _a; + FORCEINLINE DecompEmitterWindow(void *pp, unsigned = 0, const SideEffect& eff = SideEffect()) : _a(eff, (uint8_t*)pp) {} + FORCEINLINE void emitSingleByte(uint8_t c) { _a(c); } + FORCEINLINE void emitMultiByte(uint8_t c, unsigned len) + { + _a.eff.emitMultiByte(c, len); + fglcd::RAM::Memset(_a.p, c, len); + _a.p += len; + } + + template + FORCEINLINE void emitLiterals(typename Reader::Pointer rd, unsigned len) + { + Reader::ForRange_inl(rd, len, _a); + } + + void emitOffset(unsigned offset, unsigned len) + { + const uint8_t *rd = _a.p - offset; + + if(offset == 1) + emitMultiByte(*rd, len); + else + this->template emitLiterals(rd, len); + } +}; + +/* +// WARNING UNFINISHED + UNTESTED! +template +struct DecompEmitterWindow +{ + uint8_t *p; + uint8_t * const pbegin; + uint8_t * const pend; + const unsigned size; + FORCEINLINE void inc(uint8_t *& pp, unsigned x) { pp += x; while(pp >= pend) pp -= size; } + FORCEINLINE unsigned space(const uint8_t *pp) const { return pend - pp; } + FORCEINLINE DecompEmitterWindow(uint8_t *pp, unsigned sz) : p(pp), pbegin(pp), pend(pp + sz), size(sz) {} + // initial condition: pbegin <= p < pend + // aka p always points to the next valid spot to write to + FORCEINLINE void emitSingleByte(uint8_t c) { *p = c; inc(p, 1); } + FORCEINLINE void emitMultiByte(uint8_t c, unsigned len) + { + do + { + const unsigned n = vmin(space(p), len); + memset(p, c, n); + inc(p, n); + len -= n; + } + while(len); + } + template + FORCEINLINE void emitLiterals(typename Reader::Pointer buf, unsigned len) + { + Reader::SetSegmentPtr(buf); + do + { + const unsigned n = vmin(space(p), len); + Reader::Memcpy(p, buf, len); + inc(p, n); + len -= n; + } + while(len); + } + + FORCEINLINE void emitOffset(unsigned offset, unsigned len) + { + uint8_t *rd = p - offset; + if(rd < pbegin) + rd += space(pbegin); // wrap around backwards + do + { + const unsigned n = vmin3(space(p), space(rd), len); + memmove(p, rd, n); + inc(p, n); + inc(rd, n); + len -= n; + } + while(len); + } +}; +*/ + +// must be used with page-aligned ptr and exactly 256b buffer size +template +struct DecompEmitterWindow +{ + enum { WINDOWSIZE = 256 }; + union Up8 + { + uint8_t *p; + uint8_t c[sizeof(uint8_t*)]; + }; + + struct Action + { + FORCEINLINE Action(const SideEffect& eff_, uint8_t *p) : eff(eff_) + { + u.p = p ? p : CFG_PTR_DECOMPWINDOW; + FGLCD_ASSERT(!(uintptr_t(p) & (WINDOWSIZE-1)), "wtinyaln"); + } + FORCEINLINE void operator()(uint8_t x) + { + eff.emitSingleByte(x); + *u.p = x; + ++u.c[0]; + } + Up8 u; + SideEffect eff; + }; + Action _a; + + FORCEINLINE DecompEmitterWindow(void *p, unsigned = 0, const SideEffect& eff = SideEffect()) + : _a(eff, (uint8_t*)p) + {} + + FORCEINLINE void emitSingleByte(uint8_t c) { _a(c); } + + void emitMultiByte(uint8_t c, unsigned len) + { + _a.eff.emitMultiByte(c, len); + + if(uhi8(len)) // filling more than the window; just fill everything + { + _a.u.c[0] = 0; + fglcd::RAM::Memset(_a.u.p, c, WINDOWSIZE); + } + else + { + uint8_t sz = uint8_t(len); + uint8_t rem = uint8_t(0xff) - _a.u.c[0] + uint8_t(1); // space until hitting end of page + if(rem && sz > rem) // remaining space until page end + { + fglcd::RAM::Memset(_a.u.p, c, rem); + _a.u.c[0] = 0; // restart + sz -= rem; + } + if(sz) // whatever goes into new page + { + fglcd::RAM::Memset(_a.u.p, c, sz); + _a.u.c[0] += sz; + } + } + } + + template + FORCEINLINE void emitLiterals(typename Reader::Pointer rd, unsigned len) + { + Reader::ForRange_inl(rd, len, _a); + } + + void emitOffset(unsigned offset, unsigned len) + { + Up8 rd = _a.u; + rd.c[0] -= uint8_t(offset); + + if(offset == 1) + { + emitMultiByte(*rd.p, len); + return; + } + + // Need to use explicit wraparound when reading window + FGLCD_DUFF8(unsigned, len, { + _a(*rd.p); + ++rd.c[0]; + }); + } +}; + + +template struct NeedsDecompWindow +{ + static_assert(!(PackTypeProps[PK] & PP_COMPOUND), "eh?"); + static const bool value = !!(PackTypeProps[PK] & PP_HAS_WINDOW); +}; + +template struct _SelectWindowType; + +template struct _SelectWindowType +{ + static const DecompWindowType value = DECOMP_WINDOW_OUTPUT; +}; +template struct _SelectWindowType +{ + static const DecompWindowType value = (WSZ == 0) + ? DECOMP_WINDOW_NONE + : (WSZ <= 255 ? DECOMP_WINDOW_TINY : DECOMP_WINDOW_BIG); +}; + +template +struct SelectWindowType +{ + static const bool needwin = DST == ToRAM || (WSZ && NeedsDecompWindow::value); + static const DecompWindowType wintype = needwin ? _SelectWindowType::value : DECOMP_WINDOW_NONE; + + typedef typename SelectSideEffect::type SideEffect; + + typedef DecompEmitterWindow type; +}; diff --git a/democode/src/demolib/demo_def.h b/democode/src/demolib/demo_def.h new file mode 100644 index 0000000..8c6aec8 --- /dev/null +++ b/democode/src/demolib/demo_def.h @@ -0,0 +1,249 @@ +#pragma once + +#include +#include "cfg-demo.h" +#include "fglcd/def.h" +#include "fglcd/macros.h" + +#define PROGMEM_FAR FGLCD_PROGMEM_FAR + +#if defined(_MSC_VER) || defined(unix) +#define LIKELY(x) (x) +#define UNLIKELY(x) (x) +#endif + + +#ifdef __AVR__ + +#pragma GCC optimize ("O3") + +#include + +#pragma GCC diagnostic ignored "-Wattributes" +#pragma GCC diagnostic ignored "-Wunused-function" +//#pragma GCC diagnostic error "-fpermissive" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wunused-variable" + + +#define COMPILER_BARRIER() __asm__ __volatile__ ("" : : : "memory") + +// placement new decl, since #include doesn't exist on AVR-libc +inline void* operator new(size_t _Size, void* _Where) +{ + (void)_Size; + return _Where; +} + +inline void operator delete(void*, void*) +{ + return; +} + +template +struct AlignedMemory { + char mem[Size]; +} __attribute__((aligned(Align))); + + +// This is quite a hack. Since PROGMEM starts filling the lower 64k ROM area and eventually goes beyond that, +// we need a way to start filling the high area. +// The default linker script has no such section defined, but .fini0 seems to work for the purpose +// as it comes after the code section (by default it does PROGMEM, code, destructors, finiX, in that order. +// Since we don't leave main(), it's never executed. +// Same thing is in fardata.S. +#ifndef PROGMEM_FAR +#define PROGMEM_FAR __attribute__((__section__(".fini0"))) +#endif + +#ifndef PFSTR +#define PFSTR(s) (__extension__({static const char __c[] PROGMEM_FAR = (s); &__c[0];})) +#endif + +#define LIKELY(x) __builtin_expect(!!(x), 1) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) + +#endif + +#ifndef Countof +namespace internal +{ + template + char (&_ArraySizeHelper( T (&a)[N]))[N]; + + template + char (&_ArraySizeHelper(const A&))[A::N + (0&typename A::is_array_tag())]; + + template + struct NotZero { static const size_t value = n; }; + template<> + struct NotZero<0> {}; + + constexpr size_t _StrlenHelper(const char * const s) { return *s ? 1 + _StrlenHelper(s + 1) : 0; } + +} +#define Countof(a) (internal::NotZero<(sizeof(internal::_ArraySizeHelper(a)))>::value) +#define static_strlen(a) (internal::_StrlenHelper(a)) +//#define static_strlen(a) (Countof(a)-1) +#endif + +static FORCEINLINE uint8_t s8abs(int8_t x) +{ + const uint8_t mask = uint8_t(x) >> 7u; + return (mask+x)^mask; +} +static FORCEINLINE uint16_t s16abs(int16_t x) +{ + const uint16_t mask = uint16_t(x) >> 15u; + return (mask+x)^mask; +} + +template +FORCEINLINE T rotl(T v) +{ + const T BITS = sizeof(T) * 8; + const T s = SH>=0? SH%32 : -((-SH)%BITS); + return (v<>(BITS-s)); +} + +template +FORCEINLINE T rotr(T v) +{ + const T BITS = sizeof(T) * 8; + const T s = SH>=0? SH%BITS : -((-SH)%BITS); + return (v>>s) | (v<<(BITS-s)); +} + +FORCEINLINE static uint8_t ulo8(uint16_t w) +{ + union layout + { + uint16_t i; + struct { uint8_t lo, hi; } b; + } u; + u.i = w; + return u.b.lo; +} + +FORCEINLINE static uint8_t uhi8(uint16_t w) +{ + union layout + { + uint16_t i; + struct { uint8_t lo, hi; } b; + } u; + u.i = w; + return u.b.hi; +} + +FORCEINLINE static uint8_t uhh8(uint32_t w) +{ + union layout + { + uint32_t i; + struct { uint8_t lo, hi, hhi, hhhi; } b; + } u; + u.i = w; + return u.b.hhi; +} + +FORCEINLINE static uint8_t uhhh8(uint32_t w) +{ + union layout + { + uint32_t i; + struct { uint8_t lo, hi, hhi, hhhi; } b; + } u; + u.i = w; + return u.b.hhhi; +} + + +/* +template struct TFac { static const uint32_t value = x * TFac::value; }; +template<> struct TFac<0u> { static const uint32_t value = 1u; }; +template struct TPow { static const uint32_t value = x * TFac::value; }; +template struct TPow { static const uint32_t value = 1u; }; +template struct TSinPart { static const float value = m * float(TPow::value) / float(TFac::value); }; +template struct _TSinSumF { static const float value = TSinPart::value + _TSinSumF::value; }; +template struct _TSinSumF { static const float value = 0; }; +template struct TSinSumF { static const float value = _TSinSumF::value; }; + +template +static FORCEINLINE void *alignPtr(void *p) +{ + return (uint8_t*)((uint_farptr_t)p + (A - (uint_farptr_t)p % A)); +} +*/ + +template +static FORCEINLINE unsigned alignedSize(unsigned sz) +{ + return sz + A; +} + +#define fastmemcpy_PF(dst, src, n) fglcd::ProgmemFar::Memcpy(dst, fglcd_get_farptr(src), n) + +template struct remove_ref { typedef T type; }; +template struct remove_ref { typedef T type; }; +template struct remove_ref { typedef T type; }; + +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { typedef typename remove_pointer::type type; }; + +template struct remove_array { typedef T type; }; +template struct remove_array { typedef typename remove_array::type type; }; +template struct remove_array { typedef typename remove_array::type type; enum { size = N }; }; + +template struct remove_const { typedef T type; }; +template struct remove_const { typedef T& type; }; +template struct remove_const { typedef T type; }; + +template struct remove_volatile { typedef T type; }; +template struct remove_volatile { typedef T type; }; + +template struct remove_cv; +template struct remove_cv +{ + typedef + typename remove_volatile < + typename remove_const::type + >::type + type; +}; + +/* +template +struct PFArray +{ + typedef typename remove_array< + typename remove_cv< + typename remove_ref::type + >::type + >::type T; + + enum { N = remove_array::size }; + + T arr[N]; + + PFArray(fglcd::FarPtr pf) + { + fglcd::ProgmemFar::Memcpy(arr, pf, N * sizeof(T)); + } + + FORCEINLINE T& operator[](size_t i) { return arr[i]; } + FORCEINLINE const T& operator[](size_t i) const { return arr[i]; } + + FORCEINLINE operator T*() { return &arr[0]; } + FORCEINLINE operator const T*() const { return &arr[0]; } + + FORCEINLINE operator void*() { return &arr[0]; } + FORCEINLINE operator const void*() const { return &arr[0]; } + + //FORCEINLINE operator fglcd::RAM::Pointer() const { return static_cast(&arr[0]); } +}; + +#define FARARRAY(var, af) PFArray var(fglcd_get_farptr(af)) +*/ + +#define demopart NOINLINE void diff --git a/democode/src/demolib/demodebug.cpp b/democode/src/demolib/demodebug.cpp new file mode 100644 index 0000000..4fdfb83 --- /dev/null +++ b/democode/src/demolib/demodebug.cpp @@ -0,0 +1,210 @@ +#include "fglcd/lut_7seg.h" + +#include "demodebug.h" +#include "debugthing.h" +#include "globals.h" +#include "scratch.h" + +#include +#include + + +typedef fglcd::Pin LED; // for debugging + +#ifdef CFG_USE_DEBUG_THING + +extern "C" const void *__bss_end; +extern "C" const void *__bss_start; +extern "C" const void *__heap_start; +extern "C" const void *__data_start; + + +void dbg_demomain(DebugThing& dbg, void *ud, uint8_t b, uint8_t bx) +{ + DebugVars& v = *((DebugVars*)ud); + if(b & bx & 0x80) + G.demo.partdone = true; + if(b & bx & 0x40) + ++v.what; + if(DebugThing::Alone()) + { + switch(v.what) + { + default: + v.what = 0; + /* fall through */ + case 0: case 1: + { + // RAMSTART <= __bss_end <= SP <= RAMEND +#ifdef __AVR__ + const int16_t ramstart = RAMSTART; + //const int16_t heapstart = uintptr_t(__data_start); // this was __malloc_heap_start which should be the same, but pulls in some malloc() symbols that use RAM + const int16_t heapstart = ramstart + sizeof(AllGlobals); // HACK + const int16_t ramend = RAMEND; + const int16_t sp = SP; +#else + const int16_t ramstart = 0; + const int16_t heapstart = 0; + const int16_t ramend = 0x2000; + const int16_t sp = 0x100; +#endif + const int16_t staticused = heapstart - ramstart; + const int16_t stackfree = sp - heapstart; // stack pointer grows downwards + const int16_t stackused = ramend - sp; +#ifdef RAMPZ + const uint8_t rampz = RAMPZ; +#else + const uint8_t rampz = 0; +#endif + if(v.what == 0) + dbg.set2Num(stackused, stackfree); + else + dbg.set2Num(staticused, rampz); + const uint8_t nev = evs::getNumEvents(); + uint8_t led = 0; + for(uint8_t i = 0; i < nev; ++i) + { + led <<= 1u; + led |= 1u; + } + dbg.setLEDs(led); + } + break; +#ifdef CFG_ENABLE_AUDIO + case 2: + { + uint8_t haseff = 0; + uint8_t vis[8]; + memset(vis, 0, 8); + uint8_t on = 0; + for(uint8_t i = 0; i < pmfplayer_max_channels; ++i) + { + if(i < sizeof(vis)) + { + const pmf_channel_info ch = G.mus.player.channel_info(i); + on <<= 1u; + if(ch.note_hit != v.chHit[i]) + { + v.chHit[i] = ch.note_hit; + on |= 1; + } + haseff <<= 1u; + if(ch.effect != 0xff) + haseff |= 1u; + if(!(ch.base_note & 0x80)) + vis[i] = ch.base_note & (~fglcd::seg7::DOT); + } + } + dbg.setLEDs(ulo8(on)); + for(uint8_t i = 0; i < 8; ++i, haseff >>= 1u) + { + uint8_t x = vis[i]; + if(haseff & 1) + x |= fglcd::seg7::DOT; + dbg.setRaw(i, x); + } + } + break; + + case 3: + { + dbg.set2Num(G.mus.player.pattern_row(), G.mus.player.playlist_pos()); + } + break; +#endif + } + } + + dbg.commit(); + +#if defined(CFG_ENABLE_AUDIO) && defined(MCU_IS_PC) + //printf("%d / %d\n", player.pattern_row(), player.playlist_pos()); +#endif +} + + +DebugVars::DebugVars() +{ + fglcd::RAM::Memset(this, 0, sizeof(*this)); +} + +#endif // defined CFG_USE_DEBUG_THING + +#ifdef __AVR__ + +void _assert_fail(const char *cond_P, const char *msg_P, unsigned line, int val, int val2) +{ + cli(); + LED::makeOutput(); + +#ifdef CFG_USE_DEBUG_THING + for(uint8_t i = 0; i < 2; ++i) + { + G.debug.device.setLEDs(0xff); + fglcd::delay_ms(100); + G.debug.device.setLEDs(0); + fglcd::delay_ms(50); + } +#endif + +#ifdef CFG_USE_DEBUG_THING + char mmsg[9]; + const char *numstr_P = PSTR("%8d"); + + strncpy_P(mmsg, msg_P, 8); + mmsg[8] = 0; +#endif + for(;;) + { + LED::lo(); +#ifdef CFG_USE_DEBUG_THING + G.debug.device.setNum(line); + G.debug.device.setLEDs(0x0c); +#endif + fglcd::delay_ms(2000); + + LED::hi(); +#ifdef CFG_USE_DEBUG_THING + G.debug.device.setTextRAM(mmsg); + G.debug.device.setLEDs(0x30); +#endif + fglcd::delay_ms(2000); + LED::lo(); +#ifdef CFG_USE_DEBUG_THING + G.debug.device.setNum(val); + G.debug.device.setLEDs(val == val2 ? 0xc0 : 0x40); +#endif + fglcd::delay_ms(2000); +#ifdef CFG_USE_DEBUG_THING + if(val != val2) + { + G.debug.device.setNum(val2); + G.debug.device.setLEDs(0x80); + fglcd::delay_ms(2000); + } +#endif + } +} +#elif defined(MCU_IS_PC) // __AVR__ +void _assert_fail(const char *cond_P, const char *msg_P, unsigned line, int val, int val2) +{ + fprintf(stderr, "ASSERTION FAILED [%u] <%s>: %s\n[val = %d], [val2 = %d]\n", line, msg_P, cond_P, val, val2); + debug_break(); + abort(); +} +#endif + + + +void demo_init_debug() +{ +#ifdef MCU_IS_PC + printf("scratch0 = %p\n", scratch0); +#endif + FGLCD_ASSERT((uintptr_t(scratch0) & 0xff) == 0, "scratcha"); + +#ifdef CFG_USE_DEBUG_THING + DebugThing::StaticInit(); + G.debug.device.init(); +#endif +} diff --git a/democode/src/demolib/demodebug.h b/democode/src/demolib/demodebug.h new file mode 100644 index 0000000..8f81deb --- /dev/null +++ b/democode/src/demolib/demodebug.h @@ -0,0 +1,86 @@ +#pragma once + +#include "demo_def.h" + +class DebugThing; + +struct DebugVars +{ + DebugVars(); + uint8_t what; + uint8_t chHit[8]; +}; + +void demo_init_debug(); +void dbg_demomain(DebugThing& dbg, void *ud, uint8_t b, uint8_t bx); + +#ifdef MCU_IS_PC + + +#include +#include +#include + +template +static void printmat(const M& m) +{ + fglcd::NoInterrupt no; + printf("--mat4[%d]--\n", M::dynamicsize); + // fuck this shiiiit +#define G(x, y) (m.template get().tofloat()) +#define P(x, y) printf("%f ", G(x,y)); +#define R(y) P(0,y) P(1,y) P(2,y) P(3,y) puts(""); + R(0) R(1) R(2) R(3) +#undef R +#undef G +#undef P + puts("----"); +} + +template +static void formatMatRawC(std::ostream& os, const M& m) +{ + for(unsigned i = 0 ; i < M::dynamicsize; ++i) + { + if(i && !(i%6)) + os << "\n "; + os << m._dyn[i].tofloat() << "f, "; + } +} + +template +static void printmatRawC(const M& m) +{ + fglcd::NoInterrupt no; + std::cout << "{"; + formatMatRawC(std::cout, m); + std::cout << "}\n"; +} + +template +static void printvec(const V& v) +{ + fglcd::NoInterrupt no; + printf("-- vec%u (", V::elems); + // fuck this shiiiit +#define G(x) (v[x].tofloat()) +#define P(x) printf("%f ", G(x)); + for(unsigned i = 0; i < V::elems; ++i) + P(i) +#undef G +#undef P + puts(")"); +} + +#define DEBUG_PRINTF(...) printf(__VA_ARGS__) + +#else + +template +static FORCEINLINE void printmat(const M& m) {} +template +static FORCEINLINE void printvec(const V& v) {} + +#define DEBUG_PRINTF(...) /* no-op */ + +#endif diff --git a/democode/src/demolib/draw.h b/democode/src/demolib/draw.h new file mode 100644 index 0000000..28f189d --- /dev/null +++ b/democode/src/demolib/draw.h @@ -0,0 +1,4 @@ +#include "cfg-demo.h" +#include "draw_proto.h" + +typedef DrawT Draw; diff --git a/democode/src/demolib/draw_proto.h b/democode/src/demolib/draw_proto.h new file mode 100644 index 0000000..fbaad44 --- /dev/null +++ b/democode/src/demolib/draw_proto.h @@ -0,0 +1,209 @@ +#pragma once + +#include +#include +#include +#include "fglcd/fglcd.h" +#include "drawhelper.h" +#include "../demomath/fgmath.h" + +#undef abs + +template +struct DrawT +{ + typedef uint8_t u8; + typedef int8_t s8; + typedef uint16_t u16; + typedef int16_t s16; + typedef int32_t s32; + + typedef typename LCD::DimType DimType; + typedef typename LCD::TotalPixelType TotalPixelType; + typedef typename LCD::ColorType Color; + static const DimType WIDTH = LCD::WIDTH; + static const DimType HEIGHT = LCD::HEIGHT; + static const DimType XMAX = LCD::XMAX; + static const DimType YMAX = LCD::YMAX; + + static NOINLINE void palfade(Color * const pal, const u8 n, const u8 r, const u8 g, const u8 b) + { + u8 c[3]; + const u8 rgb[3] = { r, g, b }; + for(u8 i = 0; i < n; ++i) + { + LCD::splitcolor_inl(pal[i], &c[0]); + for(u8 k = 0; k < 3; ++k) + c[k] = saturateSub(c[k], rgb[k]); + pal[i] = LCD::gencolor(c[0], c[1], c[2]); + } + } + + static FORCEINLINE void fillrectfromto(u16 x, u16 y, u16 x1, u16 y1, Color color) + { + LCD::fillrectfromto(x,y,x1,y1,color); + } + + static NOINLINE void borderrectfromto(u16 x, u16 y, u16 x1, u16 y1, Color color, u8 thickness) + { + fillrectfromto(x, y, x1, y + thickness, color); + fillrectfromto(x, y+thickness, x+thickness, y1-thickness, color); + fillrectfromto(x1-thickness, y+thickness, x1, y1-thickness, color); + fillrectfromto(x, y1-thickness, x1, y1, color); + } + + // Based on http://andybrown.me.uk/2013/06/08/a-generic-16-bit-lcd-adaptor-for-the-arduino/ + // Uses two separate cases for horizontal and vertical lines to minimize the number of setxy() calls + static void drawline(s16 x0, s16 y0, s16 x1, s16 y1, Color col) + { + if(x0 == x1) + { + LCD::fillrect(x0, vmin(y0, y1), 1, vabs(y1-y0)+1, col); + return; + } + + if(y0 == y1) + { + LCD::fillrect(vmin(x0, x1), y0, vabs(x0-x1)+1, 1, col); + return; + } + + const s16 dx = abs(x1 - x0); + const s16 dy = abs(y1 - y0); + if(dy < dx) + { + if(x0 > x1) + { + vswap(x0, x1); + vswap(y0, y1); + } + //const s16 mdy = -dy; + const s16 sy = y0 < y1 ? 1 : -1; + s16 err = dx - dy; + /*for( ; x0 < 0; ) + { + const s16 err2 = err << 1; + err -= dy;. + ++x0; + if(err2 < dx) + { + err += dx; + y0 += sy; + } + }*/ + const s16 xmax = vmin(x1, XMAX); + //const s16 ymax = lcd::YMAX; + //NoInterrupt no; + for(;;) + { + LCD::setxy(x0, y0, x1, y0); + LCD::setColor(col); + fastcontx: + LCD::sendPixel(); + if(x0 == xmax /*|| x0 > int(XMAX)*/) + break; + const s16 err2 = err << 1; + //if(err2 > mdy) + { + err -= dy; + ++x0; + } + if(err2 < dx) + { + err += dx; + y0 += sy; + /*if(y0 > int(YMAX)) + return;*/ + } + else + goto fastcontx; + } + } + else + { + if(y0 > y1) + { + vswap(x0, x1); + vswap(y0, y1); + } + //const s16 mdx = -dx; + const s16 sx = x0 < x1 ? 1 : -1; + s16 err = dy - dx; + + /*for( ; y0 < 0; ) + { + const s16 err2 = err << 1; + err -= dx; + ++y0; + if(err2 < dy) + { + err += dy; + x0 += sx; + } + }*/ + + //const s16 xmax = lcd::XMAX; + const s16 ymax = vmin(y1, YMAX); + //NoInterrupt no; + for(;;) + { + LCD::setxy(x0, y0, x0, y1); + LCD::setColor(col); + fastconty: + LCD::sendPixel(); + if(y0 == ymax /*|| y0 > int(YMAX)*/) + break; + const s16 err2 = err << 1; + //if(err2 > mdx) + { + err -= dx; + ++y0; + } + if(err2 < dy) + { + err += dy; + x0 += sx; + /*if(x0 > int(XMAX)) + return;*/ + } + else + goto fastconty; + } + } + } + + template + static FORCEINLINE void drawimageRaw(DimType x, DimType y, DimType w, DimType h, typename ImgReader::Pointer img) + { + // FIXME: use AABB clipping to screen + LCD::setxywh_inl(x, y, w, h); + _drawimageloopX16(img, TotalPixelType(w) * TotalPixelType(h)); + } + + template + static FORCEINLINE void drawimageRaw(typename ImgReader::Pointer img, TotalPixelType n) + { + // FIXME: use AABB clipping to screen + _drawimageloopX16(img, n); + } + + template + static NOINLINE void _drawimageloopX16(typename ImgReader::Pointer img, TotalPixelType n) + { + typename ImgReader::SegmentBackup seg; + ImgReader::SetSegmentPtr(img); + + const u16 nx8 = u16(n / 8u); + u8 rem = u8(n) % u8(8); + for(u8 k = 0; k < 8; ++k) + { + FGLCD_DUFF8(DimType, nx8, { + const u8 p = ImgReader::readIncFast(img); + LCD::sendPixel(palgetcolor_inl(p)); + }); + } + if(rem) do + LCD::sendPixel(palgetcolor_inl(ImgReader::readIncFast(img))); + while(--rem); + } +}; // end Draw diff --git a/democode/src/demolib/drawfont.cpp b/democode/src/demolib/drawfont.cpp new file mode 100644 index 0000000..6d8241f --- /dev/null +++ b/democode/src/demolib/drawfont.cpp @@ -0,0 +1,125 @@ +#include "drawfont.h" + +namespace drawfont { + +CharDef getCharDef(uint8_t c, const FontDef& fnt) +{ + const uint8_t idx = fnt.index[c]; + // If this assert fails, an attempt was made to print a glyph that doesn't exist in the font, + // likely optimized away by the packer. Regenerate font files properly! + FGLCD_ASSERT(idx != 0xff, "fontidx"); + const uint16_t offs = fnt.offp[idx]; + typename fglcd::RAM::Pointer p = fnt.data + offs; + const uint8_t w = fglcd::RAM::readIncFast(p); + const uint8_t h = fglcd::RAM::readIncFast(p); + const CharDef cdef { w, h, p }; + return cdef; +} + +void metrics(LCD::PixelPos& pp, const char c, const FontDef& fnt, const LCD::DimType maxwidth) +{ + FGLCD_ASSERT(c != 13, "dos2unix"); // make SURE to have UNIX file endings in text files!! + if(LIKELY(c != '\n')) + { + CharDef cdef = getCharDef(c, fnt); + if(UNLIKELY(pp.x+cdef.w >= maxwidth)) + goto newline; + pp.x += cdef.w; + } + else + { + newline: + pp.y += fnt.height; + pp.x = 0; + } +} + +void buildindex_PF(unsigned char *index, fglcd::FarPtr rd, uint8_t n) +{ + typename fglcd::ProgmemFar::SegmentBackup seg; +#ifdef _DEBUG + fglcd::RAM::Memset(index, 0xff, 256); +#endif + fglcd::ProgmemFar::SetSegmentPtr(rd); + for(uint8_t i = 0; i < n; ++i) + { + const uint8_t p = fglcd::ProgmemFar::readIncFast(rd); + index[p] = i; + } +} + + +} // end namespace drawfont + + +FontTyperBase::FontTyperBase(const char *text_, Callback cb_, void *udcb_, DrawFunc draw_, void *uddraw_) + : cb(cb_), udcb(udcb_), draw(draw_), uddraw(uddraw_), text(text_) + , _done(0), _haschar(0), _nextchar(0) +{ +} + +FontTyperBase::~FontTyperBase() +{ + if(_done != 1) + { + _done = 2; + while(_done != 1) {} + } +} + +void FontTyperBase::start(uint16_t delay) +{ + if(delay) + schedule(delay); + else + _tick(); +} + + +void FontTyperBase::ev_tick(void *p) +{ + ((FontTyperBase*)p)->_tick(); +} + +void FontTyperBase::schedule(uint16_t ms) +{ + if(!_done) + evs::schedule(ms, ev_tick, this); +} + +void FontTyperBase::_tick() +{ + if(_done) + { + _done = 1; + return; + } + _nextchar = *text++; + _haschar = 1; +} + +void FontTyperBase::_enterAndSchedule(char c) +{ + if(uint16_t t = _enter(c)) + schedule(t); + else + _done = 1; +} + +uint16_t FontTyperBase::_enter(char c) +{ + uint16_t t = cb(c, pos, udcb); + if(c) + draw(c, pos, uddraw); + return t; +} + +void FontTyperBase::update() +{ + if(_haschar) + { + _haschar = 0; + _enterAndSchedule(_nextchar); + } + +} \ No newline at end of file diff --git a/democode/src/demolib/drawfont.h b/democode/src/demolib/drawfont.h new file mode 100644 index 0000000..bddb462 --- /dev/null +++ b/democode/src/demolib/drawfont.h @@ -0,0 +1,284 @@ +#pragma once + +#include "demo_def.h" +#include "fontdef.h" +#include "drawhelper.h" +#include "decomp.h" + +namespace drawfont { + +// Minimal API required to draw a font +// Instead of this we could use 'LCD' directly, but this indirection is to catch errors +struct DrawToLCD +{ + typedef LCD::ColorType ColorType; + typedef LCD::DimType DimType; + + static const DimType WIDTH = LCD::WIDTH; + + FORCEINLINE static void setColor(Color col) + { + LCD::setColor(col); + } + + FORCEINLINE static void fastfill_u8(uint8_t n) + { + LCD::fastfill_u8(n); + } + + FORCEINLINE static void setxywh(DimType x, DimType y, DimType w, DimType h) + { + LCD::setxywh_inl(x, y, w, h); + } +}; + + +template +FORCEINLINE static void drawLZ4count(Out& out, typename Reader::Pointer& src, uint8_t n, typename Out::ColorType col) +{ + out.setColor(col); + out.fastfill_u8(n); + if(n == 0xf) + { + do + { + n = Reader::readIncFast(src); + out.fastfill_u8(n); + } + while(n == 0xff); + } +} + +// just emit pixels; assumes that the drawing rect is already set up +template +FORCEINLINE static void draw_1bpp_lz4counts_npix_inl(Out& out, typename Reader::Pointer src, typename Out::ColorType fg, typename Out::ColorType bg) +{ + typename Reader::SegmentBackup seg; + Reader::SetSegmentPtr(src); + for(uint8_t c; (c = Reader::readIncFast(src)); ) // zero byte marks end of run + { + drawLZ4count(out, src, c & 0xf, bg); + drawLZ4count(out, src, c >> 4u, fg); + } +} + +struct FontDef +{ + uint8_t *index; + uint16_t *offp; + typename fglcd::RAM::Pointer data; + uint8_t height; +}; + +struct CharDef +{ + uint8_t w, h; + const uint8_t *datastart; +}; + + +CharDef getCharDef(uint8_t c, const FontDef& fnt); + +// Assumes that font data have been decompressed to RAM +// Modifies pp +template +static void drawchar(Out& out, typename LCD::PixelPos& pp, const char c, typename Out::ColorType fg, typename Out::ColorType bg, const FontDef& fnt, typename Out::DimType startx) +{ + FGLCD_ASSERT(c != 13, "dos2unix"); // make SURE to have UNIX file endings in text files!! + if(LIKELY(c != '\n')) + { + CharDef cdef = getCharDef(c, fnt); + if(UNLIKELY(pp.x+cdef.w >= LCD::WIDTH)) + goto newline; + out.setxywh(pp.x, pp.y, cdef.w, cdef.h); + pp.x += cdef.w; + drawfont::draw_1bpp_lz4counts_npix_inl(out, cdef.datastart, fg, bg); + } + else + { +newline: + pp.y += fnt.height; + pp.x = startx; + } +} + +void metrics(LCD::PixelPos& pp, const char c, const FontDef& fnt, const LCD::DimType maxwidth); + +template +static NOINLINE typename LCD::PixelPos drawfontloop_noinl( + Out& out, + typename LCD::PixelPos pp, + typename ReadStr::Pointer str, typename Out::ColorType fg, typename Out::ColorType bg, + const FontDef& fnt +) { + typename ReadStr::SegmentBackup seg; + const typename Out::DimType startx = pp.x; + for(char c; (c = ReadStr::readIncSafe(str)); ) + drawchar(out, pp, c, fg, bg, fnt, startx); // modifies pp + return pp; +} + +template +static NOINLINE LCD::PixelPos calcfontsize_noinl( + typename ReadStr::Pointer str, + const FontDef& fnt, + LCD::DimType maxwidth +) { + typename ReadStr::SegmentBackup seg; + LCD::PixelPos pp; + pp.x = 0; + pp.y = 0; + LCD::DimType xmax = 0; + uint8_t prev = 0; + for(char c; (c = ReadStr::readIncSafe(str)); ) + { + metrics(pp, c, fnt, maxwidth); // modifies pp + xmax = vmax(xmax, pp.x); + } + if(!pp.y) + pp.y = fnt.height; + return LCD::PixelPos(xmax, pp.y); +} + +void buildindex_PF(unsigned char *index, fglcd::FarPtr rd, uint8_t n); + +} // end namespace drawfont + + +// Unpack Font data to RAM. Needs an external index array of exactly 256 bytes. +template +struct DecompFontData : _DecompDataStorage +{ + //static_assert(!TFont::blocked, "blocked font does not make sense"); + typedef _DecompDataStorage Storage; + typedef typename Storage::Mem Mem; + uint16_t ramoffs[TFont::noffsets]; // way better to keep this in RAM than in progmem + DecompFontData(uint8_t *index) + { + this->_prepare(); + fastmemcpy_PF(ramoffs, TFont::offsets, sizeof(ramoffs)); // HMM: if sizeof(void*) == sizeof(ramoffs[0]), we could do dirty ptr aliasing + offset delta-encoding, saving some more space, probably + buildIndex(index); + } + void buildIndex(uint8_t *index) + { + fglcd::ProgmemFar::Pointer rd = fglcd_get_farptr(TFont::usedch); + drawfont::buildindex_PF(index, rd, TFont::nusedch); + } +}; + +template +struct DrawFont : public DecompFontData +{ + typedef DecompFontData Base; + typedef Out_ Out; + typedef typename LCD::PixelPos PP; + typedef typename Out::ColorType Color; + + Out *_out; + + // index must be exactly 256 bytes large + FORCEINLINE DrawFont(unsigned char *index, Out *out = 0) + : Base(index) + , _out(out) + { + fnt.index = index; + fnt.offp = &this->ramoffs[0]; + fnt.height = TFont::fontheight; + fnt.data = this->ptr(); + } + + FORCEINLINE void rebuildIndex() { this->buildIndex(fnt.index); } + + FORCEINLINE uint8_t lineHeight() const { return TFont::fontheight; } + + template + FORCEINLINE PP drawStr(PP pp, typename Mem::VoidPointer ptr, Color fg, Color bg) const + { + typename Mem::Pointer str = (typename Mem::Pointer)ptr; + return drawfont::drawfontloop_noinl(*_out, pp, str, fg, bg, fnt); + } + FORCEINLINE PP drawChar(PP pp, char c, Color fg, Color bg, typename Out::DimType startx) const + { + drawfont::drawchar(*_out, pp, c, fg, bg, fnt, startx); // modifies pp + return pp; + } + + // bounding box of text, considers \n to start new lines and + // retains the maximum x extent seen on a line + template + FORCEINLINE PP calcSize(typename Mem::VoidPointer ptr) const + { + typename Mem::Pointer str = (typename Mem::Pointer)ptr; + return drawfont::calcfontsize_noinl(str, fnt, Out::WIDTH); + } + +private: + drawfont::FontDef fnt; +}; + +struct FontTyperBase +{ + // callback can modify the char to be printed. set c=0 to not print char. + // return time to wait (in ms) until next char. return 0 to stop typing. + typedef uint16_t (*Callback)(char& c, LCD::PixelPos pp, void *ud); + + // modify pixel pos to where to print next + typedef void (*DrawFunc)(char c, LCD::PixelPos& pp, void *ud); + + Callback cb; + void *udcb; + DrawFunc draw; + void *uddraw; + LCD::PixelPos pos; + LCD::DimType startx; + const char *text; // modifies text in place! + + FontTyperBase(const char *text_, Callback cb_, void *udcb_, DrawFunc draw_, void *uddraw_); + ~FontTyperBase(); + void start(uint16_t delay); // start in background; eventsystem will do the drawing + void update(); // ONLY call this when bg==false was passed to ctor. returns true when something was printed + static void ev_tick(void *p); + void schedule(uint16_t ms); + + FORCEINLINE void setPos(LCD::DimType x, LCD::DimType y) + { + pos.x = x; + pos.y = y; + startx = x; + } + + FORCEINLINE uint8_t done() const + { + return _done; + } + +private: + void _tick(); + volatile uint8_t _done, _haschar; + volatile char _nextchar; + uint16_t _enter(char c); + void _enterAndSchedule(char c); +}; + +template // DrawFont +struct FontTyper : public FontTyperBase +{ + typedef FontTyper Self; + typedef typename DF::Out Out; + typedef typename DF::Color Color; + typedef typename DF::PP PP; + + const DF& df; + Color fgcol, bgcol; // set this after ctor + + static void s_draw(char c, PP& pp, void *ud) + { + Self *self = (Self*)ud; + pp = self->df.drawChar(pp, c, self->fgcol, self->bgcol, self->startx); + } + + FontTyper(const DF& df_, const char *text_, Callback cb_, void *udcb_) + : FontTyperBase(text_, cb_, udcb_, s_draw, this), df(df_), fgcol(Color(-1)), bgcol(0) + { + } +}; diff --git a/democode/src/demolib/drawhelper.cpp b/democode/src/demolib/drawhelper.cpp new file mode 100644 index 0000000..da2b717 --- /dev/null +++ b/democode/src/demolib/drawhelper.cpp @@ -0,0 +1,180 @@ +#include "drawhelper.h" +#include "scratch.h" +#include "../demomath/fgmath.h" + +#ifdef __GNUC__ +#pragma GCC optimize ("Os") +#endif + + +void palsetcolor(uint8_t b, uint16_t c) +{ +#ifdef FGLCD_ASM_AVR + void *x; + uint8_t *pal = CFG_PAL_START_LO; + asm volatile( + "movw %A[x], %[pal] \n\t" + "add %A[x], %[b] \n\t" + "st %a[x], %A[c] \n\t" +#if CFG_PAL_SIZE == 256 + "inc %B[x] \n\t" +#elif CFG_PAL_SIZE < 256 + "add %A[x],%[offs] \n\t" +#endif + "st %a[x], %B[c]" + : [x] "=&x" (x) /*outputs*/ + : [b] "r" (b), [pal] "r" (pal), [c] "r" (c) /* inputs */ +#if CFG_PAL_SIZE < 256 + , [offs] "r" (CFG_PAL_SIZE) +#endif + ); +#else + CFG_PAL_START_LO[b] = ulo8(c); + CFG_PAL_START_HI[b] = uhi8(c); +#endif +} + + +void applyPal16_PF(fglcd::FarPtr pal, uint16_t n, uint8_t paloffs) +{ + for(uint16_t i = 0; i < n; ++i) + palsetcolor(uint8_t(i+paloffs), fglcd::ProgmemFar::template read(pal, i)); +} +void applyPal16_RAM(const uint16_t * pal, uint16_t n, uint8_t paloffs) +{ + for(uint16_t i = 0; i < n; ++i) + palsetcolor(uint8_t(i+paloffs), fglcd::RAM::template read(pal, i)); +} + +void clearpal(Color c) +{ + uint8_t i = 0; + do + palsetcolor(i, c); + while(++i); +} + +void fuckpal() +{ + clearpal(LCD::gencolor_inl(0xff, 0, 0xff)); +} + +void shadepal(uint8_t shadelevels, uint8_t mul, uint8_t offs, const Color *base, uint8_t npal) +{ + if(!shadelevels) + { + applyPal16_RAM(base, npal, offs); + return; + } + uint8_t paloffs = shadelevels + offs; + for(uint8_t z = 0; z < npal; ++z) + { + uint8_t c[3]; + LCD::splitcolor(base[z], &c[0]); + for(uint8_t i = 0; i < shadelevels; ++i) + { + uint8_t dark[3], light[3]; + const uint16_t m16 = mul * i; + const uint8_t m = m16 > 0xff ? 0xff : uint8_t(m16); + for(uint8_t k = 0; k < 3; ++k) + { + //dark[k] = (uint8_t)vmax(0, int16_t(c[k]) - m); + //light[k] = (uint8_t)vmin(0xff, int16_t(c[k]) + m); + dark[k] = saturateSub(c[k], m); + light[k] = saturateAdd(c[k], m); + } + const uint8_t t = (z*shadelevels*2) + paloffs; + palsetcolor(t-i, LCD::gencolor(dark[0], dark[1], dark[2])); + palsetcolor(t+i, LCD::gencolor(light[0], light[1], light[2])); + } + } + + /* + union Col3 + { + uint8_t r,g,b; + uint8_t a[3]; + }; + uint8_t idx = shadelevels + offs; + const uint8_t adv = 2 * shadelevels; + for(uint8_t i = 0; i < npal; ++i, idx += adv) + { + const uint16_t col = fglcd::ProgmemFar::read(pal, i); + palsetcolor(idx, col); + Col3 cc; + LCD::splitcolor(col, cc.a); + for(uint8_t k = 0; k < shadelevels; ++k) + { + for(uint8_t x = 0; x < Countof(cc.a); ++x) + cc.a[x] = dim8(cc.a[x]); + palsetcolor(idx-k, LCD::gencolor(cc.r, cc.g, cc.b)); + } + LCD::splitcolor(col, cc.a); + for(uint8_t k = 0; k < shadelevels; ++k) + { + for(uint8_t x = 0; x < Countof(cc.a); ++x) + cc.a[x] = brighten8(vmin(cc.a[x], uint8_t(1))); + palsetcolor(idx+k, LCD::gencolor(cc.r, cc.g, cc.b)); + } + } + */ +} + +void shadepal_PF(uint8_t shadelevels, uint8_t mul, uint8_t offs, fglcd::FarPtr base, uint8_t npal) +{ + Color *tmp = (Color*)StackAlloc(npal * sizeof(Color)); + fglcd::ProgmemFar::Memcpy(tmp, base, npal * sizeof(Color)); + shadepal(shadelevels, mul, offs, tmp, npal); +} + +PalBackup::PalBackup() +{ + fglcd::RAM::Memcpy(col, CFG_PAL_START_LO, sizeof(col)); +} + +PalBackup::~PalBackup() +{ + fglcd::RAM::Memcpy(CFG_PAL_START_LO, col, sizeof(col)); +} + +Color dampenColor(Color c, uint8_t sub) +{ + uint8_t col[3]; + LCD::splitcolor(c, col); + for(uint8_t k = 0; k < 3; ++k) + col[k] = saturateSub(col[k], sub); + return LCD::gencolor(col[0], col[1], col[2]); +} + +Color brightenColor(Color c, uint8_t add) +{ + uint8_t col[3]; + LCD::splitcolor(c, col); + for(uint8_t k = 0; k < 3; ++k) + col[k] = saturateAdd(col[k], add); + return LCD::gencolor(col[0], col[1], col[2]); +} + +// assumes backup of the palette right after n colors +void dampenColors(uint8_t start, uint8_t sub, uint8_t n) +{ + const uint8_t end = start + n; + for(uint8_t i = start; i < end; ++i) + { + uint16_t c = palgetcolor_noinl(i + n); + c = dampenColor(c, sub); + palsetcolor(i, c); + } +} + +// assumes backup of the palette right after n colors +void brightenColors(uint8_t start, uint8_t add, uint8_t n) +{ + const uint8_t end = start + n; + for(uint8_t i = start; i < end; ++i) + { + uint16_t c = palgetcolor_noinl(i + n); + c = brightenColor(c, add); + palsetcolor(i, c); + } +} diff --git a/democode/src/demolib/drawhelper.h b/democode/src/demolib/drawhelper.h new file mode 100644 index 0000000..57f6b34 --- /dev/null +++ b/democode/src/demolib/drawhelper.h @@ -0,0 +1,123 @@ +#pragma once + +#include "demo_def.h" +#include "fglcd/mcu.h" +#include "scratch.h" + + +#if CFG_SCRATCH_ALIGNMENT != 256 +#error fix this +#endif + +#if CFG_PAL_SIZE > 256 +#error fix this +#endif + +FORCEINLINE static uint16_t palgetcolor_inl(uint8_t b) +{ +#ifdef FGLCD_ASM_AVR + uint16_t c; + void *x; + const uint8_t * const pal = CFG_PAL_START_LO; + // asm version takes advantage of page-alignment of the scratch buffers + asm volatile( + "movw %A[x], %[pal] \n\t" + "add %A[x], %[b] \n\t" + "ld %A[c],%a[x] \n\t" +#if CFG_PAL_SIZE == 256 + "inc %B[x] \n\t" +#elif CFG_PAL_SIZE < 256 + "add %A[x],%[offs] \n\t" +#endif + "ld %B[c],%a[x]" + : [c] "=&r" (c), [x] "=&x" (x) /*outputs*/ /* or possibly "=&e" to use any ptr reg */ + : [b] "r" (b), [pal] "r" (pal) /* inputs */ +#if CFG_PAL_SIZE < 256 + , [offs] "r" (CFG_PAL_SIZE) +#endif + ); + return c; + #else + return CFG_PAL_START_LO[b] | (CFG_PAL_START_HI[b] << 8u); + #endif +} + +NOINLINE static uint16_t palgetcolor_noinl(uint8_t b) +{ + return palgetcolor_inl(b); +} + +void applyPal16_PF(fglcd::FarPtr pal, uint16_t n, uint8_t paloffs = 0); +void applyPal16_RAM(const uint16_t *pal, uint16_t n, uint8_t paloffs = 0); +void palsetcolor(uint8_t b, uint16_t c); +void fuckpal(); // debug shit +void clearpal(Color c); + +#define APPLYPAL_PF(pf, offs) applyPal16_PF(fglcd_get_farptr(pf), Countof(pf), (offs)); + +template +struct _PalApplyHelper +{ + static NOINLINE void Apply(uint8_t offs) + { + static_assert(TImage::npal == npal, "eh"); + static_assert(TImage::npal + TImage::paloffs < CFG_PAL_SIZE, "pal too large"); + applyPal16_PF(fglcd_get_farptr(TImage::pal), TImage::npal, TImage::paloffs + offs); + } +}; +template +struct _PalApplyHelper +{ + static FORCEINLINE void Apply(uint8_t) {} +}; + +// also works for meshes +template +FORCEINLINE void applyPal16_Image(uint8_t offs = 0) +{ + _PalApplyHelper::Apply(offs); +} + +// use (2 * shadelevels) + 1 colors for each original color in the mesh. +// assuming 2 original colors A, B, with shadelevels == 2, they will be ordered like this: +// A--, A-, A, A+, A++, B--, B-, B, B+, B++ +// Where +, - means dimmed/brightened color. +// Distance between adjacent colors of the same shade level is [2*shadelevel]. +// The first original color is located at index [shadelevel + offset]. +void shadepal (uint8_t shadelevels, uint8_t mul, uint8_t offs, const Color *base, uint8_t npal); +void shadepal_PF(uint8_t shadelevels, uint8_t mul, uint8_t offs, fglcd::FarPtr base, uint8_t npal); + +template +struct _MeshPalApplyHelper +{ + static NOINLINE void Apply(uint8_t shadelevels, uint8_t mul, uint8_t offs) + { + static_assert(TMesh::npal == npal, "eh"); + static_assert(TMesh::npal + TMesh::paloffs < CFG_PAL_SIZE, "pal too large"); + shadepal_PF(shadelevels, mul, offs, fglcd_get_farptr(TMesh::pal), TMesh::npal); + } +}; +template +struct _MeshPalApplyHelper +{ + static FORCEINLINE void Apply(uint8_t, uint8_t, uint8_t) {} +}; + +template +FORCEINLINE void applyPal16_Mesh(uint8_t shadelevels, uint8_t mul, uint8_t offs) +{ + _MeshPalApplyHelper::Apply(shadelevels, mul, offs); +} + +struct PalBackup +{ + uint8_t col[CFG_PAL_SIZE * sizeof(Color)]; + PalBackup(); + ~PalBackup(); +}; + +Color dampenColor(Color c, uint8_t sub); +Color brightenColor(Color c, uint8_t add); + +void dampenColors(uint8_t start, uint8_t sub, uint8_t n); +void brightenColors(uint8_t start, uint8_t add, uint8_t n); diff --git a/democode/src/demolib/drawmesh.cpp b/democode/src/demolib/drawmesh.cpp new file mode 100644 index 0000000..9e5e3e6 --- /dev/null +++ b/democode/src/demolib/drawmesh.cpp @@ -0,0 +1,8 @@ +#include "drawmesh.h" + + +void AddToRegion(uint8_t *p, uint8_t N, int8_t offs) +{ + for(uint8_t i = 0; i < N; ++i) + p[i] += offs; +} diff --git a/democode/src/demolib/drawmesh.h b/democode/src/demolib/drawmesh.h new file mode 100644 index 0000000..2819fb4 --- /dev/null +++ b/democode/src/demolib/drawmesh.h @@ -0,0 +1,227 @@ +#pragma once + +#include "decomp.h" +#include "vertex.h" +#include "raster.h" +#include "render.h" + +void AddToRegion(uint8_t *p, uint8_t N, int8_t offs); + +// Unpack Mesh to RAM +template +struct DecompMeshData : public _DecompDataStorage, public TMesh +{ + typedef uint8_t IndexType; // this is hardcoded. don't change. no time to adjust the rest of the code, deadline.... + + static_assert(!TMesh::blocked, "blocked mesh does not make sense"); + static_assert(TMesh::Ntris && TMesh::Nverts, "mesh must have tris & verts"); + typedef _DecompDataStorage Storage; + + static const unsigned ColorOffs = 0; + static const unsigned ColorSize = TMesh::Ntris; + static const unsigned IndexOffs = ColorOffs + ColorSize; + static const unsigned IndexSize = TMesh::Indexed ? (3 * TMesh::Ntris * sizeof(IndexType)) : 0; + static const unsigned VertexOffs = IndexOffs + IndexSize; + static const unsigned VertexSize = TMesh::Nverts * sizeof(svec3); + static const unsigned ReqSize = VertexOffs + VertexSize; + + static const unsigned _fullsize = TMesh::fullsize; + static_assert(_fullsize == ReqSize, "size mismatch"); + static_assert(sizeof(Storage) >= ReqSize, "too small"); // possible that storage has extra bytes + + uint8_t _shadelevels, _upshade, _downshade; + DecompMeshData() + { + this->_prepare(); + _shadelevels = 0; + } + + FORCEINLINE void applyColorOffs(int8_t offs) + { + AddToRegion(this->ptr() + ColorOffs, TMesh::Ntris, offs); + } + + FORCEINLINE void applypal(uint8_t shadelevels, uint8_t mul, uint8_t upshade, uint8_t downshade, uint8_t offs = 0) + { + applyPal16_Mesh(shadelevels, mul, offs); + _shadelevels = shadelevels; + _upshade = upshade; + _downshade = downshade; + } + FORCEINLINE void applypalNoShade(uint8_t offs = 0) + { + applyPal16_Mesh(0, 0, offs); + _shadelevels = 0; + _upshade = 0; + _downshade = 0; + } + FORCEINLINE const uint8_t *getColorData() const { return static_cast(this->ptr() + ColorOffs); } + FORCEINLINE const raster::Tri *getTriData() const { return IndexSize ? reinterpret_cast(this->ptr() + IndexOffs) : NULL; } + FORCEINLINE const svec3 *getVertexData() const { return reinterpret_cast(this->ptr() + VertexOffs); } + + FORCEINLINE uint8_t *getColorData() { return static_cast(this->ptr() + ColorOffs); } + FORCEINLINE raster::Tri *getTriData() { return IndexSize ? reinterpret_cast(this->ptr() + IndexOffs) : NULL; } + FORCEINLINE svec3 *getVertexData() { return reinterpret_cast(this->ptr() + VertexOffs); } +}; + +template +struct _MeshClipBuf +{ + FORCEINLINE uint8_t *ptr() { return NULL; } + FORCEINLINE const uint8_t *ptr() const { return NULL; } +}; + +template +struct _MeshClipBuf +{ + uint8_t clipbuf[TMesh::Nverts]; + FORCEINLINE const uint8_t *ptr() const { return &clipbuf[0]; } + FORCEINLINE uint8_t *ptr() { return &clipbuf[0]; } +}; + +template +struct _MeshZBuf +{ + FORCEINLINE vtx::Zval *ptr() { return NULL; } + FORCEINLINE const vtx::Zval *ptr() const { return NULL; } +}; + +template +struct _MeshZBuf +{ + vtx::Zval zbuf[N]; + FORCEINLINE const vtx::Zval *ptr() const { return &zbuf[0]; } + FORCEINLINE vtx::Zval *ptr() { return &zbuf[0]; } +}; + +// TODO: pass initial transform to ctor so we don't have to do this per-frame? + +enum DrawMeshFlags // bitmask +{ + DM_NONE = 0, + DM_CLIP = 1, // pass to clip during transform and filtering + DM_STOREZ = 2, // pass to record vertex z during transform and be able to sort +}; + +template +struct DrawMesh : public DecompMeshData +{ + typedef vtx::Zval Zval; + typedef DecompMeshData Base; + ivec2 projbuf[TMesh::Nverts]; // projected positions of vertices in screen space + uint8_t filterlist[TMesh::Ntris]; // indexes of: frontface tris at the start, backface tris at the end + + // Only allocate clipping space if we're actually going to perform clipping. Saves 1 byte per vertex. + _MeshClipBuf clipbuf; + // Same for per-vertex Z buffer, another byte + _MeshZBuf zbufvert; + +#ifdef _DEBUG + DrawMesh() + { + printf("DrawMesh[tri:%u, vert:%u] size = %u\n", TMesh::Ntris, TMesh::Nverts, unsigned(sizeof(*this))); + } +#endif + + // FIRST STEP + // transform vertices with model-specific scaling factor, project into screen space + template + FORCEINLINE void transform(const M& m, const ivec2& viewport = ivec2(0)) // FIXME: remove default viewport + { + mat4ds mds(TMesh::scale()); + vtx::transform_and_project(projbuf, clipbuf.ptr(), zbufvert.ptr(), this->getVertexData(), m * mds, TMesh::Nverts, viewport); + } + + // this is ok to use after transform + FORCEINLINE raster::AABB getAABB() const + { + return raster::getVertexListAABB(projbuf, TMesh::Nverts); + } + + // SECOND STEP + // Categorize triangles into front- or backface and does clipping. + // Use after transform. + FORCEINLINE raster::TriFilterResult filter() + { + return raster::filterTris(filterlist, projbuf, clipbuf.ptr(), this->getTriData(), TMesh::Ntris); + } + // then you can get those: + FORCEINLINE uint8_t *frontFaceIdxs() { return &filterlist[0]; } + FORCEINLINE uint8_t *backFaceIdxs(uint8_t nBack) { return &filterlist[Countof(filterlist) - nBack]; } + // or, alternatively, no filter: + // (this one can be run just once at init but does not do clipping) + FORCEINLINE raster::TriFilterResult noFilter() + { + for(uint16_t i = 0; i < TMesh::Ntris; ++i) + filterlist[i] = i; + raster::TriFilterResult res; + res.nBack = 0; + res.nFront = TMesh::Ntris; + return res; + } + + FORCEINLINE raster::Tri getTri(const uint8_t *triidxs, uint16_t i) const + { + return this->getTriData()[triidxs[i]]; + } + + // OPTIONAL THIRD STEP + FORCEINLINE void sortIndexZ(uint8_t *triidxs, uint16_t n) + { +#ifdef _DEBUG + assert(n < 256); +#endif + raster::sortTrisZ(filterlist, (uint8_t)n, this->getTriData(), TMesh::Ntris, zbufvert.ptr()); + } + + template + FORCEINLINE void sortIndex(uint8_t *triidxs, uint16_t n, const LESS& cmp) + { +#ifdef _DEBUG + assert(n < 256); +#endif + raster::sortTris(triidxs, (uint8_t)n, this->getTriData(), cmp); + } + + // LAST STEP + FORCEINLINE void draw(const uint8_t *triidxs, uint16_t n, const raster::Params& rp, uint8_t yoffs, uint8_t coloffs) const + { + render::ToLCD render(rp.rubmul, rp.glitchmul); + raster::drawTriangles(render, triidxs, n, projbuf, this->getTriData(), this->getColorData(), zbufvert.ptr(), rp, yoffs, this->_shadelevels, this->_upshade, this->_downshade, coloffs); + } + + FORCEINLINE void drawToTex(const uint8_t *triidxs, uint16_t n, const raster::Params& rp, uint8_t yoffs, uint8_t coloffs, uint8_t *tex, uint8_t w, uint8_t h) const + { + render::ToTex render(tex, w, h); + raster::drawTriangles(render, triidxs, n, projbuf, this->getTriData(), this->getColorData(), zbufvert.ptr(), rp, yoffs, this->_shadelevels, this->_upshade, this->_downshade, coloffs); + } + + FORCEINLINE void drawWireframe(const uint8_t *triidxs, uint16_t n, Color col) const + { + raster::drawWireframe(triidxs, n, col, this->getTriData(), projbuf); + } + + FORCEINLINE void drawWireframeZ_DEBUG(const uint8_t *triidxs, uint16_t n) + { + for(uint16_t k = 0; k < n; ++k) + { + uint8_t ci = k * (256 / (n+1)); + Color cc = LCD::gencolor(ci,ci,ci); + drawWireframe(triidxs + k, 1, cc); + } + } + + FORCEINLINE void drawZ_DEBUG(const uint8_t *triidxs, uint16_t n, const raster::Params& rp, uint8_t yoffs, uint8_t coloffs) + { + PalBackup bk; + + for(uint16_t k = 0; k < n; ++k) + { + uint8_t ci = k * (256 / (n+1)); + Color cc = LCD::gencolor(ci,ci,ci); + for(unsigned i = 0; i < 256; ++i) + palsetcolor(uint8_t(i), cc); + draw(triidxs + k, 1, rp, yoffs, coloffs); + } + } +}; diff --git a/democode/src/demolib/eventsystem.cpp b/democode/src/demolib/eventsystem.cpp new file mode 100644 index 0000000..f309d12 --- /dev/null +++ b/democode/src/demolib/eventsystem.cpp @@ -0,0 +1,224 @@ +// The event system uses Timer 1 + interrupt +// The scheduler runs at millisecond granularity + +#include "eventsystem.h" +#include "cfg-demo.h" +#include "globals.h" +#include "../demomath/fgmath.h" + +#if CFG_MAX_EVENTS+0 + +#ifdef __AVR__ + +#include +#include +#include +#include "demo_def.h" + +typedef evs::priv_Event Event; + +static const long clocksPerMillisec = F_CPU / 1000UL; +static const unsigned MAX_TIMER_VALUE = 0xffff; + +#if CFG_USE_HIGH_PRECISION_EVENT_TIMER+0 +#define enableTimer() \ + TCCR1B = _BV(CS11) | _BV(CS10) | (1 << WGM12); // start clock with /64 prescaler +static const long PRESCALER = 64; +#else +#define enableTimer() \ + TCCR1B = _BV(CS12) | _BV(CS10) | (1 << WGM12); // start clock with /1024 prescaler + CTC mode +static const long PRESCALER = 1024; +#endif + + +#define disableTimer() \ + TCCR1B = (1 << WGM12); + +#define enableTimerInterrupt() \ + TIMSK1 = (1 << OCIE1A); // enable interrupt on match (== OCR2A) + +#define disableTimerInterrupt() \ + TIMSK1 = 0; + +static const long ticksPerMillisec = clocksPerMillisec / PRESCALER; +#define msToTicks(ms) ( long(ms) * ticksPerMillisec ) +#define ticksToMS(ticks) ( (ticks) / ticksPerMillisec ) + +void evs::init() +{ + G.ev.inISR = 0; + G.ev.numEv = 0; + G.ev.numNewEvents = 0; + G.ev.waittime = MAX_TIMER_VALUE; + + disableTimer(); + TCCR1A = 0; + TCNT1 = 0; + OCR1A = MAX_TIMER_VALUE; + enableTimerInterrupt(); +} + +static void updateTimer(unsigned ms) +{ + const unsigned ticks = (unsigned)vmin(msToTicks(ms), MAX_TIMER_VALUE); + G.ev.waittime = ticksToMS(ticks); + TCNT1 = 0; + OCR1A = ticks; +} + +void evs::schedule(unsigned ms, callback f, void * ud) +{ + FGLCD_ASSERT(ms, "esched0"); // makes problems somehow + if(!G.ev.inISR) + { + const uint8_t oldTIMSK1 = TIMSK1; + disableTimerInterrupt(); + __builtin_avr_nop(); // allow any pending interrupt to trigger + const uint8_t n = G.ev.numEv; + volatile Event *ev = &G.ev.events[n]; // write to front + ev->remain = ms; + ev->f = f; + ev->ud = ud; + G.ev.numEv = n + 1; + if(ms <= G.ev.waittime) // <= is important, if this is the first task waittime == 0xffff, and if ms is also == 0xffff, this would not properly set the timer + updateTimer(ms); + enableTimer(); + TIMSK1 = oldTIMSK1; // re-enable timer interrupt if previously enabled. may fire interrupt right away if ms == 0 + } + else + { + G.ev.waittime = vmin(G.ev.waittime, ms); + const uint8_t nn = G.ev.numNewEvents; + G.ev.numNewEvents = nn + 1; + volatile Event *ev = &G.ev.events[Countof(G.ev.events)-1 - nn]; // write to back + ev->remain = ms; + ev->f = f; + ev->ud = ud; + } +} + +unsigned char evs::getNumEvents() +{ + return G.ev.numEv; +} + +void evs::killAllEvents() +{ + G.ev.numEv = 0; +} + +static void isr_updateEvents(uint8_t N) +{ + // N = Number of currently active events (not including those that got added inside of this very interrupt invocation) + // numEv is not touched until the very end of the ISR. + disableTimerInterrupt(); // leave other interrupts untouched -- do NOT disable globally since there might be more important ISRs to run + unsigned wt = MAX_TIMER_VALUE; // allow pending interrupts to trigger + COMPILER_BARRIER(); + ++G.ev.inISR; + const unsigned timepassed = G.ev.waittime; + for(uint8_t i = 0; i < N; ) + { + volatile Event *ev = &G.ev.events[i]; + unsigned remain = ev->remain; + if(remain > timepassed) + { + // Event is not yet due; update time and move to next + remain -= timepassed; + wt = vmin(wt, remain); + ev->remain = remain; + ++i; + continue; + } + // --- Time to run the callback --- + // Get data and store in registers + evs::callback const f = ev->f; + void * const ud = ev->ud; + // One less now + --N; + // Move top event over current + volatile Event *top = &G.ev.events[N]; + ev->remain = top->remain; + ev->f = top->f; + ev->ud = top->ud; + + G.ev.waittime = wt; // f() call may spawn new events, which depends on waittime set correctly + f(ud); + wt = G.ev.waittime; // re-fetch, as this may have changed + } + const uint8_t newEv = G.ev.numNewEvents; + if(newEv) + { + memmove((void*)&G.ev.events[N], (const void*)&G.ev.events[Countof(G.ev.events) - newEv], newEv * sizeof(Event)); + N += newEv; + G.ev.numNewEvents = 0; + } + G.ev.numEv = N; + if(N) + { + updateTimer(wt); + } + else + { + disableTimer(); + G.ev.waittime = unsigned(-1); + } + --G.ev.inISR; + enableTimerInterrupt(); +} + +ISR(TIMER1_COMPA_vect, ISR_NOBLOCK) +{ + uint8_t N = G.ev.numEv; + if(N) + isr_updateEvents(N); +} + +#elif defined(MCU_IS_PC) // end __AVR__ + +static SDL_atomic_t numev = { 0 }; + +struct PassCB +{ + evs::callback f; + void *ud; +}; + +static Uint32 _sdl_timer_thunk(Uint32 interval, void *pass) +{ + PassCB *p = (PassCB*)pass; + SDL_AtomicAdd(&numev, -1); + SDL_MemoryBarrierAcquire(); // yolo + p->f(p->ud); + SDL_MemoryBarrierRelease(); + delete p; + return 0; +} + +void evs::init() { SDL_AtomicSet(&numev, 0); } +void evs::schedule(unsigned ms, callback f, void *ud) +{ + FGLCD_ASSERT(ms, "esched0"); + unsigned evnow = SDL_AtomicAdd(&numev, 1) + 1; + FGLCD_ASSERT(evnow < CFG_MAX_EVENTS, "evs overload"); + SDL_AddTimer(ms, _sdl_timer_thunk, new PassCB { f, ud }); +} +unsigned char evs::getNumEvents() +{ + return SDL_AtomicGet(&numev); +} +void evs::killAllEvents() +{ + SDL_AtomicSet(&numev, 0); +} + +#endif // MCU_IS_PC + +#else + +namespace evs { +unsigned char getNumEvents() { return 0; } +void init() {} +void schedule(unsigned ms, callback f, void *ud) {} +} + +#endif diff --git a/democode/src/demolib/eventsystem.h b/democode/src/demolib/eventsystem.h new file mode 100644 index 0000000..4d0c479 --- /dev/null +++ b/democode/src/demolib/eventsystem.h @@ -0,0 +1,29 @@ +#pragma once + +#include "cfg-demo.h" + +namespace evs { + +typedef void (*callback)(void *); + +struct priv_Event +{ + unsigned remain; + evs::callback f; + void *ud; +}; + +struct priv_Globals +{ + volatile uint8_t numEv, numNewEvents; + volatile uint8_t inISR; + volatile priv_Event events[CFG_MAX_EVENTS]; + volatile unsigned waittime; +}; + + +void init(); +void schedule(unsigned ms, callback f, void *ud); +unsigned char getNumEvents(); +void killAllEvents(); // NEVER call this from an event function +} diff --git a/democode/src/demolib/farload.h b/democode/src/demolib/farload.h new file mode 100644 index 0000000..0e956ce --- /dev/null +++ b/democode/src/demolib/farload.h @@ -0,0 +1,27 @@ +#pragma once + +#include "demo_def.h" + +template +struct _FarLoadHelper +{ + typedef int is_array_tag; + enum { N = N_ }; + T data[N]; + FORCEINLINE T& operator[](size_t i) { return data[i]; } + FORCEINLINE const T& operator[](size_t i) const { return data[i]; } + FORCEINLINE T* operator*() { return &data[0]; } + FORCEINLINE const T* operator*() const { return &data[0]; } + FORCEINLINE T* ptr() { return &data[0]; } + FORCEINLINE const T* ptr() const { return &data[0]; } +}; + +template +FORCEINLINE _FarLoadHelper _farload(fglcd::FarPtr p) +{ + _FarLoadHelper ret; + fglcd::ProgmemFar::Memcpy(&ret.data[0], p, N * sizeof(T)); + return ret; +} + +#define farload(a) (_farload::type, Countof(a)>(fglcd_get_farptr(a))) diff --git a/democode/src/demolib/fastlookup.cpp b/democode/src/demolib/fastlookup.cpp new file mode 100644 index 0000000..3c93b77 --- /dev/null +++ b/democode/src/demolib/fastlookup.cpp @@ -0,0 +1,54 @@ +#include "fastlookup.h" +#include "demo_def.h" +#include "fglcd/fglcd.h" +#include "../demomath/fgmath.h" + +void loadIsinToScratch2() +{ + fglcd::ProgmemFar::Memcpy(CFG_PTR_ISINTAB, fglcd_get_farptr(lut_isintab), 256); +} + +void loadUsinToScratch3() +{ + fglcd::ProgmemFar::Memcpy(CFG_PTR_USINTAB, fglcd_get_farptr(lut_isintab), 256); + uint8_t *a = (uint8_t*)CFG_PTR_USINTAB; + uint8_t i = 0; + do + *a++ += 127; + while(++i); +} + +fp1616 FSmoothSinCosScratch::sin(fp88 x) +{ + int16_t a = ISIN8FAST(x.intpart()) << 8; + if(uint8_t m = x.mantissa()) + { + int16_t b = ISIN8FAST(x.intpart() + 1) << 8; + a = lerp_inl(a, b, m); + } + fp1616 ret = fp1616::raw(int32_t(a) * 2); + return ret; +} +fp1616 FSmoothSinCosScratch::cos(fp88 x) +{ + int16_t a = ICOS8FAST(x.intpart()) << 8; + if(uint8_t m = x.mantissa()) + { + int16_t b = ICOS8FAST(x.intpart() + 1) << 8; + a = lerp_inl(a, b, m); + } + fp1616 ret = fp1616::raw(int32_t(a) * 2); + return ret; +} + + +// needs 2 scratch pages +/* +void loadFP88sinToScratch2and3() +{ + fp88 *p = (fp88*)CFG_PTR_ISINTAB; + uint8_t i = 0; + do + *p++ = isin8slow(i) / 127.0f; + while(++i); +}*/ diff --git a/democode/src/demolib/fastlookup.h b/democode/src/demolib/fastlookup.h new file mode 100644 index 0000000..232d972 --- /dev/null +++ b/democode/src/demolib/fastlookup.h @@ -0,0 +1,112 @@ +#pragma once + +#include "../demomath/lookup.h" +#include "scratch.h" + +template +static FORCEINLINE T *pageselect(T *p, uint8_t page) +{ + union + { + T *p; + char c[sizeof(void*)]; + } u; + u.p = p; + u.c[1] += page; + return u.p; +} + +template +static FORCEINLINE T *pageindex(T *p, uint8_t i) +{ + union + { + T *p; + char c[sizeof(void*)]; + } u; + u.p = p; + u.c[0] += i; + return u.p; +} + +// same as p[i & 0xff], but saves one AVR clock cycle when p is 256 byte aligned +template +static FORCEINLINE T pagelookup(const void *p, uint8_t i) +{ + return *pageindex(static_cast(p), i); +} + +void loadIsinToScratch2(); +void loadUsinToScratch3(); + + + +#ifdef _MSC_VER +#define _ucosptr (CFG_PTR_USINTAB+64u) +#define _icosptr (CFG_PTR_ISINTAB+64u) +#else +static constexpr const uint8_t * const _ucosptr = CFG_PTR_USINTAB+64u; +static constexpr const uint8_t * const _icosptr = CFG_PTR_ISINTAB+64u; +#endif + +#define USIN8FAST(i) pagelookup(CFG_PTR_USINTAB, uint8_t(i)) +#define UCOS8FAST(i) pagelookup(_ucosptr, uint8_t(i)) +#define ISIN8FAST(i) pagelookup(CFG_PTR_ISINTAB, uint8_t(i)) +#define ICOS8FAST(i) pagelookup(_icosptr, uint8_t(i)) + + + +/* +struct FSinCosScratchFP88 +{ + typedef uint8_t angle_type; + static FORCEINLINE fp88 sin(uint8_t a) + { + //return (ISIN8FAST(a) / 127.0f); + fp88 *p = (fp88*)CFG_PTR_ISINTAB; + return p[a]; + } + static FORCEINLINE fp88 cos(uint8_t a) + { + //return (ICOS8FAST(a) / 127.0f); + fp88 *p = (fp88*)CFG_PTR_ISINTAB; + return p[(a+64) & 0xff]; + } +}; +*/ + +// This is super incorrect, but still gets the job done for rotation matrices etc. +struct FSinCosScratch +{ + typedef uint8_t angle_type; + static FORCEINLINE fp88 sin(uint8_t a) + { + return fp88::raw(ISIN8FAST(a) * uint8_t(2)); // important that sign extension happens + } + static FORCEINLINE fp88 cos(uint8_t a) + { + return fp88::raw(ICOS8FAST(a) * uint8_t(2)); + } +}; + +struct FSinCosPROGMEM +{ + typedef uint8_t angle_type; + static FORCEINLINE fp88 sin(uint8_t a) + { + return fp88::raw(isin8slow(a) * uint8_t(2)); // important that sign extension happens + } + static FORCEINLINE fp88 cos(uint8_t a) + { + return fp88::raw(icos8slow(a) * uint8_t(2)); + } +}; + +// higher precision by lerp between two consecutive sin/cos values +struct FSmoothSinCosScratch +{ + typedef fp88 angle_type; + static fp1616 sin(fp88 x); + static fp1616 cos(fp88 x); +}; + diff --git a/democode/src/demolib/fglcd/button.h b/democode/src/demolib/fglcd/button.h new file mode 100644 index 0000000..f49986d --- /dev/null +++ b/democode/src/demolib/fglcd/button.h @@ -0,0 +1,123 @@ +#pragma once + +#if 0 // FIXME: this is too mad and impractical to use + +#include "mcu.h" + + +namespace fglcd { + +template +class DButtonBase +{ +public: + static const u8 debounce = debounce_; + mutable u8 _dc; + FORCEINLINE bool pressed() const { return _dc >= debounce; } + FORCEINLINE operator bool() const { return pressed(); } +protected: + DButtonBase() : _dc(0) {} + bool update() const + { + const bool down = MOD::get(static_cast(this)->_getButton()); + u8 c = _dc; + if(down && c < debounce) + _dc = c + 1; + else if(!down && c) + _dc = c - 1; + return pressed(); + } +}; + +template +class DButtonBase +{ +public: + DButtonBase() {} + bool pressed() const { return MOD::get(static_cast(this)->get()); } + FORCEINLINE operator bool() const { return pressed(); } + bool update() const { return pressed(); } +}; + +struct _ButtonNoMod +{ + static FORCEINLINE bool get(bool x) { return x; }; +}; + +struct _ButtonInvertMod +{ + static FORCEINLINE bool get(bool x) { return !x; }; +}; + +template +class StaticFuncButtonBase : public DButtonBase, MOD, debounce> +{ +public: + static FORCEINLINE bool _getButton() { return func(); } +}; + +template +class PinButtonBase : public DButtonBase, MOD, debounce> +{ +public: + typedef PIN Pin; + static_assert(has_pin_tag::value, "not a pin"); +}; + +/* +template +class MemberFuncButtonBase : public DButtonBase, MOD, debounce> +{ +public: + MemberFuncButtonBase(T& obj, M *mth) : _obj(&obj), _mth(mth) {} + FORCEINLINE bool _getButton() { return *_obj.*_mth(); } + +private: + T * const _obj; + M * const _mth; +}; +*/ +template +class CallableFuncButtonBase : public DButtonBase, MOD, debounce> +{ +public: + CallableFuncButtonBase(T& c) : _callable(c) {} + FORCEINLINE bool _getButton() { return _callable(); } + +private: + T& _callable; +}; + + +template +struct ButtonTypeSelector +{ + typedef typename TypeSwitch::value, + PinButtonBase, + typename TypeSwitch::value, + CallableFuncButtonBase, + void + >::type + >::type type; +}; + +template +class LowButton : public ButtonTypeSelector<_ButtonInvertMod, debounce, Ts...>::type +{ +public: + typedef typename ButtonTypeSelector<_ButtonInvertMod, debounce, Ts...>::type Self; + using Self::Self; +}; + +template +class HighButton : public ButtonTypeSelector<_ButtonNoMod, debounce, Ts...>::type +{ +public: + typedef typename ButtonTypeSelector<_ButtonNoMod, debounce, Ts...>::type Self; + using Self::Self; +}; + +} + +#endif + diff --git a/democode/src/demolib/fglcd/chip.h b/democode/src/demolib/fglcd/chip.h new file mode 100644 index 0000000..75811f8 --- /dev/null +++ b/democode/src/demolib/fglcd/chip.h @@ -0,0 +1,127 @@ +#pragma once + +#include "mcu.h" +#include "util.h" +#include "interface.h" + +namespace fglcd { + +template +struct Command_Exclusive +{ + typedef bool is_command_tag; + static_assert(has_trigger_tag::value, "CmdTrigger is not trigger"); + static_assert(has_connection_tag::value, "CmdConnection is not connection"); + typedef typename CmdConnection::value_type value_type; + + static FORCEINLINE void begin(value_type cmd) + { + CmdTrigger::set(); + CmdConnection::send(cmd); + CmdTrigger::clear(); + } + static FORCEINLINE void param(value_type cmd) + { + CmdConnection::send(cmd); + } +}; + +template +struct ChipCtrl +{ + static_assert(has_command_tag::value, "CMD type does not have is_command_tag"); + typedef typename Cmd::value_type CmdType; + + template + static void sendtable(typename FROM::VoidPointer pp) + { + typename FROM::Pointer p = static_cast(pp); + for(u8 c; (c = _readinc(p)) ; ) + { + Cmd::begin(_readinc(p)); + for(u8 i = 1; i < c; ++i) + sendparam8(_readinc(p)); + } + } + static FORCEINLINE void sendcmd(CmdType com) + { + Cmd::begin(com); + } + static FORCEINLINE void sendparam8(u8 x) + { + Cmd::param(x); + } + static FORCEINLINE void sendparam16(u16 a) + { + union + { + struct { u8 lo, hi; } pair; + u16 both; + } u; + u.both = a; + sendparam8(u.pair.hi); + sendparam8(u.pair.lo); + } + + +private: + template + static FORCEINLINE CmdType _readinc(typename FROM::Pointer& p) + { + return FROM::template read(p++); + } +}; + +template +struct ChipBase : public TSpecs +{ + typedef TSpecs Specs; + typedef SizeConfig _SizeCfg; + typedef typename _SizeCfg::type TotalPixelType; + static const TotalPixelType TOTAL_PIXELS = _SizeCfg::total; + + typedef typename Iface::ResetPin ResetPin; + typedef typename Iface::DCxPin DCxPin; + typedef typename Iface::CSPin CSPin; + + typedef typename Iface::CmdCon _CmdCon; + typedef typename Iface::DataCon _DataCon; + typedef typename _CmdCon::TriggerPin _CmdTriggerPin; + typedef typename _DataCon::TriggerPin _DataTriggerPin; + + typedef typename Specs::template ResetTriggerType ResetTrigger; + typedef typename Specs::template DCxTriggerType DCxTrigger; + typedef typename Specs::template WrxCmdTriggerType<_CmdTriggerPin> _WrxCmdTrigger; + typedef typename Specs::template WrxDataTriggerType<_DataTriggerPin> _WrxDataTrigger; + typedef typename Specs::template CSTriggerType CSTrigger; + + typedef Connection<_CmdCon, _WrxCmdTrigger> CmdConnection; + typedef Connection<_DataCon, _WrxDataTrigger> DataConnection; + + typedef Command_Exclusive Cmd; // TODO: should be param + + typedef ChipCtrl Ctrl; + + static void init() + { + ResetTrigger::clear(); + CSTrigger::clear(); + DCxTrigger::clear(); + _CmdCon::Port::makeOutput(); + _DataCon::Port::makeOutput(); + ResetPin::makeOutput(); + DCxPin::makeOutput(); + CSPin::makeOutput(); + _CmdTriggerPin::makeOutput(); + _DataTriggerPin::makeOutput(); + + Iface::init(); + } + + static FORCEINLINE void reset() { ResetTrigger::trigger(); } + static FORCEINLINE void enableCS() { CSTrigger::set(); } + static FORCEINLINE void disableCS() { CSTrigger::clear(); } +}; + + +} // end namespace fglcd diff --git a/democode/src/demolib/fglcd/chip_hx8357.h b/democode/src/demolib/fglcd/chip_hx8357.h new file mode 100644 index 0000000..4eabbf6 --- /dev/null +++ b/democode/src/demolib/fglcd/chip_hx8357.h @@ -0,0 +1,120 @@ +#pragma once + +// Most of this sourced from https://github.com/Bodmer/TFT_HX8357 + +// UNTESTED because my display broke + +#include "chip_mipi_dcs.h" + +namespace fglcd { + +struct HX8357_Specs : public MIPI_DCS_Specs +{ + // reset needs a bit of wait time before the screen is fully initialized + template struct ResetTriggerType : public TriggerWaitMS, 10>, 10> {}; + // safer to use a LowTrigger, but this is a tad faster + //template struct DCxTriggerType : public ToggleTrigger {}; + + typedef u16 DimType; + static const DimType LONG_SIZE = 480; + static const DimType SHORT_SIZE = 320; + + enum ChipSpecific + { + enable_extension = 0xB9, + set_vcom_voltage = 0xB6, + set_power_control = 0xB1, + setstba = 0xC0, + set_display_cycle = 0xB4, + set_gamma_curve = 0xE0, + memory_access_control = 0x36, + }; +}; + +template +struct HX8357_Base : public MIPI_DCS_Generic +{ + typedef HX8357_Specs S; + typedef MIPI_DCS_Generic Base; + typedef ChipBase HW; + using Op = S::Opcode; + using X = S::ChipSpecific; + + static void _init_HX8357(const u8 *revtab) + { + static const u8 generictab[] PROGMEM = + { + 2, Op::set_address_mode, S::ADDRMODE_LANDSCAPE | S::ADDRMODE_BGR, + 2, Op::set_pixel_format, S::PIXELFORMAT_16_BPP, + 0 + }; + + Base::init_sequence(&revtab[0]); + Base::init_sequence(&generictab[0]); + Base::enableCS(); + enableDisplay(); + Base::disableCS(); + } + + static FORCEINLINE void enableDisplay() + { + Base::enableDisplay(); + } + + static FORCEINLINE void disableDisplay() + { + Base::disableDisplay(); + } + + // TODO: write me + static FORCEINLINE void slow_refresh() { } + static FORCEINLINE void fast_refresh() { } + + +}; + + +template +struct HX8357C : public HX8357_Base +{ + typedef HX8357_Specs S; + typedef HX8357_Base Base; + using X = S::ChipSpecific; + + static void init() + { + static const u8 tab[] PROGMEM = + { + 4, X::enable_extension, 0xFF, 0x83, 0x57, // 3 magic bytes according to datasheet + 2, X::set_vcom_voltage, 0x2C, + 7, X::set_power_control, 0x00, 0x15, 0x0D, 0x0D, 0x83, 0x48, + 7, X::setstba, 0x24, 0x24, 0x01, 0x3C, 0xC8, 0x08, + 8, X::set_display_cycle, 0x02, 0x40, 0x00, 0x2A, 0x2A, 0x0D, 0x4F, + 35, X::set_gamma_curve, 0x00, 0x15, 0x1D, 0x2A, 0x31, 0x42, 0x4C, 0x53, 0x45, 0x40, 0x3B, 0x32, 0x2E, 0x28, + 0x24, 0x03, 0x00, 0x15, 0x1D, 0x2A, 0x31, 0x42, 0x4C, 0x53, 0x45, 0x40, 0x3B, 0x32, + 0x2E, 0x28, 0x24, 0x03, 0x00, 0x01, + 0 + }; + + Base::_init_HX8357(tab); + } + + static FORCEINLINE void enableDisplay() + { + Base::enableDisplay(); + } + + static FORCEINLINE void disableDisplay() + { + Base::disableDisplay(); + } + + // TODO: write me + static FORCEINLINE void slow_refresh() { } + static FORCEINLINE void fast_refresh() { } + + +}; + + +} // end namespace fglcd diff --git a/democode/src/demolib/fglcd/chip_ili9341.h b/democode/src/demolib/fglcd/chip_ili9341.h new file mode 100644 index 0000000..3232fe6 --- /dev/null +++ b/democode/src/demolib/fglcd/chip_ili9341.h @@ -0,0 +1,162 @@ +#pragma once + +#if 0 +// TODO check this init sequence: +// Via https://github.com/tuupola/esp-ili9341/blob/master/ili9341.c +DRAM_ATTR static const lcd_init_cmd_t lcd_init_cmds[]={ + /* Power contorl B, power control = 0, DC_ENA = 1 */ + {ILI9341_PWCTRB, {0x00, 0x83, 0X30}, 3}, + /* Power on sequence control, + * cp1 keeps 1 frame, 1st frame enable + * vcl = 0, ddvdh=3, vgh=1, vgl=2 + * DDVDH_ENH=1 + */ + {ILI9341_PWONCTR, {0x64, 0x03, 0X12, 0X81}, 4}, + /* Driver timing control A, + * non-overlap=default +1 + * EQ=default - 1, CR=default + * pre-charge=default - 1 + */ + {ILI9341_DRVTCTRA, {0x85, 0x01, 0x79}, 3}, + /* Power control A, Vcore=1.6V, DDVDH=5.6V */ + {ILI9341_PWCTRA, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5}, + /* Pump ratio control, DDVDH=2xVCl */ + {ILI9341_PUMPRTC, {0x20}, 1}, + /* Driver timing control, all=0 unit */ + {ILI9341_TMCTRA, {0x00, 0x00}, 2}, + /* Power control 1, GVDD=4.75V */ + {ILI9341_PWCTR1, {0x26}, 1}, + /* Power control 2, DDVDH=VCl*2, VGH=VCl*7, VGL=-VCl*3 */ + {ILI9341_PWCTR2, {0x11}, 1}, + /* VCOM control 1, VCOMH=4.025V, VCOML=-0.950V */ + {ILI9341_VMCTR1, {0x35, 0x3E}, 2}, + /* VCOM control 2, VCOMH=VMH-2, VCOML=VML-2 */ + {ILI9341_VMCTR2, {0xBE}, 1}, + /* Memory access contorl, MX=MY=0, MV=1, ML=0, BGR=1, MH=0 */ + //{ILI9341_MADCTL, {0x28}, 1}, + {ILI9341_MADCTL, {0x08}, 1}, // for M5Stack + /* Pixel format, 16bits/pixel for RGB/MCU interface */ + {ILI9341_PIXFMT, {0x55}, 1}, // 0b01010101 ie. 16 bits per pixel + /* Frame rate control, f=fosc, 70Hz fps */ + {ILI9341_FRMCTR1, {0x00, 0x1B}, 2}, + /* Enable 3 gamma control, disabled */ + {ILI9341_3GENABLE, {0x08}, 1}, + /* Gamma set, curve 1 */ + {ILI9341_GAMMASET, {0x01}, 1}, + /* Positive gamma correction */ + {ILI9341_GMCTRP1, {0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00}, 15}, + /* Negative gamma correction */ + {ILI9341_GMCTRN1, {0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F}, 15}, + /* Column address set, SC=0, EC=0xEF */ + {ILI9341_CASET, {0x00, 0x00, 0x00, 0xEF}, 4}, + /* Page address set, SP=0, EP=0x013F */ + {ILI9341_PASET, {0x00, 0x00, 0x01, 0x3f}, 4}, + /* Memory write */ + {ILI9341_RAMWR, {0}, 0}, + /* Entry mode set, Low vol detect disabled, normal display */ + {ILI9341_ENTRYMODE, {0x07}, 1}, + /* Display function control */ + {ILI9341_DFUNCTR, {0x0A, 0x82, 0x27, 0x00}, 4}, + /* Sleep out */ + {ILI9341_SLPOUT, {0}, 0x80}, + /* Display on */ + {ILI9341_DISPON, {0}, 0x80}, + /* End of commands . */ + {0, {0}, 0xff}, +}; +#endif + +#include "chip_mipi_dcs.h" + +namespace fglcd { + +struct ILI9341_Specs : public MIPI_DCS_Specs +{ + template struct ResetTriggerType : public TriggerWaitMS, 10>, 120> {}; + // safer to use a LowTrigger, but this is a tad faster + template struct DCxTriggerType : public ToggleTrigger {}; + + typedef u16 DimType; + static const DimType LONG_SIZE = 320; + static const DimType SHORT_SIZE = 240; + + enum ChipSpecific + { + todo = 0 + /*power_control_1 = 0xC0, + power_control_2 = 0xC1, + power_control_3_normal_mode = 0xC2, + power_control_4_idle_mode = 0xC3, + power_control_5_partial_mode = 0xC4, + vcom_control = 0xC5, + display_function_control = 0xB6, + positive_gamma_control = 0xE0, + negative_gamma_control = 0xE1, + memory_access_control = 0x36,*/ + }; +}; + +template +struct ILI9341 : public MIPI_DCS_Generic +{ + typedef ILI9341_Specs S; + typedef MIPI_DCS_Generic Base; + using Op = S::Opcode; + using X = S::ChipSpecific; + + static void init() + { + static const u8 tab[] PROGMEM = + { + 1+3, 0xF6, 0x01, 0x01, 0x00, //Interface Control needs EXTC=1 MV_EOR=0, TM=0, RIM=0 + 1+3, 0xCF, 0x00, 0x81, 0x30, //Power Control B [00 81 30] + 1+4, 0xED, 0x64, 0x03, 0x12, 0x81, //Power On Seq [55 01 23 01] + 1+3, 0xE8, 0x85, 0x10, 0x78, //Driver Timing A [04 11 7A] + 1+5, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02, //Power Control A [39 2C 00 34 02] + 1+1, 0xF7, 0x20, //Pump Ratio [10] + 1+2, 0xEA, 0x00, 0x00, //Driver Timing B [66 00] + 1+1, 0xB0, 0x00, //RGB Signal [00] + 1+2, 0xB1, 0x00, 0x1B, //Frame Control [00 1B] + // 0xB6, 2, 0x0A, 0xA2, 0x27, //Display Function [0A 82 27 XX] .kbv SS=1 + 1+1, 0xB4, 0x00, //Inversion Control [02] .kbv NLA=1, NLB=1, NLC=1 + 1+1, 0xC0, 0x21, //Power Control 1 [26] + 1+1, 0xC1, 0x11, //Power Control 2 [00] + 1+2, 0xC5, 0x3F, 0x3C, //VCOM 1 [31 3C] + 1+1, 0xC7, 0xB5, //VCOM 2 [C0] + 1+1, 0x36, 0x48, //Memory Access [00] + 1+1, 0xF2, 0x00, //Enable 3G [02] + 1+1, 0x26, 0x01, //Gamma Set [01] + 1+15,0xE0, 0x0f, 0x26, 0x24, 0x0b, 0x0e, 0x09, 0x54, 0xa8, 0x46, 0x0c, 0x17, 0x09, 0x0f, 0x07, 0x00, + 1+15,0xE1, 0x00, 0x19, 0x1b, 0x04, 0x10, 0x07, 0x2a, 0x47, 0x39, 0x03, 0x06, 0x06, 0x30, 0x38, 0x0f, + + + //2, Op::set_address_mode, S::ADDRMODE_LANDSCAPE | (1<<3), + 2, Op::set_pixel_format, S::PIXELFORMAT_16_BPP, + 0 + }; + + Base::init_sequence(&tab[0]); + delay_ms(120); + Base::enableCS(); + enableDisplay(); + Base::disableCS(); + } + + static FORCEINLINE void enableDisplay() + { + Base::enableDisplay(); + } + + static FORCEINLINE void disableDisplay() + { + Base::disableDisplay(); + } + + // TODO: write me + static FORCEINLINE void slow_refresh() { } + static FORCEINLINE void fast_refresh() { } + + +}; + +} // end namespace fglcd diff --git a/democode/src/demolib/fglcd/chip_ili9481.h b/democode/src/demolib/fglcd/chip_ili9481.h new file mode 100644 index 0000000..264634a --- /dev/null +++ b/democode/src/demolib/fglcd/chip_ili9481.h @@ -0,0 +1,132 @@ +#pragma once + +#include "chip_mipi_dcs.h" + +namespace fglcd { + +struct ILI9481_Specs : public MIPI_DCS_Specs +{ + // this chip doesn't seem to need a wait time after a reset signal + template struct ResetTriggerType : public TriggerHoldMS, 10> {}; + + // safer to use a LowTrigger, but this is a tad faster + template struct DCxTriggerType : public ToggleTrigger {}; + + typedef u16 DimType; + static const DimType LONG_SIZE = 480; + static const DimType SHORT_SIZE = 320; + + enum ChipSpecific + { + power_setting = 0xD0, + vcom_control = 0xD1, + set_gamma_table = 0xC8, + power_setting_normal_mode = 0xD2, + power_setting_idle_mode = 0xd4, + panel_driving_setting = 0xC0, + low_power_mode_control = 0xb1, + set_display_and_memory_mode = 0xb4, + display_timing_setting_normal_mode = 0xc1, + frame_rate_control = 0xc5, + }; +}; + +template +struct ILI9481 : public MIPI_DCS_Generic +{ + typedef ILI9481_Specs S; + typedef MIPI_DCS_Generic Base; + using Op = S::Opcode; + using X = S::ChipSpecific; + + enum Framerate : u8 + { + HZ_125, + HZ_100, + HZ_85, + HZ_72, + HZ_56, + HZ_50, + HZ_45, + HZ_42, + + HZ_DEFAULT = HZ_85 + }; + + static void init() + { + static const constexpr u8 tab[] PROGMEM = + { + 4, X::vcom_control, 0x00, 0x07, 0x10, + 3, X::power_setting_normal_mode, 0x01, 0x02, + 3, X::power_setting_idle_mode, 0x00, 0x77, + 6, X::panel_driving_setting, 0x10, 0x3B, 0x00, 0x02, 0x11, + 13, X::set_gamma_table, 0x00, 0x32, 0x36, 0x45, 0x06, 0x16, 0x37, 0x75, 0x77, 0x54, 0x0C, 0x00, + 2, Op::set_address_mode, uint8_t(S::ADDRMODE_LANDSCAPE) | uint8_t(S::ADDRMODE_BGR), + 2, Op::set_pixel_format, S::PIXELFORMAT_16_BPP, + //1, Op::set_display_and_memory_mode, (1<<0) | (0 << 4), + 0 + }; + + Base::init_sequence(&tab[0]); + Base::enableCS(); + enableDisplay(); + Base::disableCS(); + } + + static void enableDisplay() + { + static const u8 tab[] PROGMEM = + { + 4, X::power_setting, 0x07, 0x42, 0x18, + 2, X::low_power_mode_control, 0x00, + 0 + }; + Base::enableDisplay(); + Base::template sendtable(&tab[0]); + } + + static void disableDisplay() + { + Base::disableDisplay(); + static const u8 tab[] PROGMEM = + { + 4, X::power_setting, 0x00, 0x02, 0x10, + 2, X::low_power_mode_control, 0x01, + 0 + }; + Base::template sendtable(&tab[0]); + } + + static FORCEINLINE void slow_refresh() { Base::sendcmd3(X::display_timing_setting_normal_mode, (1<<4)|3, 31, 0xff); } + static FORCEINLINE void fast_refresh() { Base::sendcmd3(X::display_timing_setting_normal_mode, (1<<4), 16, (8 << 4) | 8); } + + static FORCEINLINE void set_refresh_rate(Framerate hz) { Base::sendcmd1(X::frame_rate_control, hz); } + + /* + static void normalPowerMode() + { + static const u8 tab[] PROGMEM = + { + 4, Op::vcom_control, 0x00, 0x07, 0x10, + 3, Op::power_setting_normal_mode, 0x01, 0x02, + 4, Op::power_setting, 0x07, 0x42, 0x18, + 0 + }; + sendtable(&tab[0]); + } + + static void lowPowerMode() + { + static const u8 tab[] PROGMEM = + { + 4, Op::vcom_control, 0x00, 0x00, 0x00, + 3, Op::power_setting_normal_mode, 0x07, 0x02, + 4, Op::power_setting, 0x01, 0x47, 0x11, + 0 + }; + sendtable(&tab[0]); + }*/ +}; + +} // end namespace fglcd diff --git a/democode/src/demolib/fglcd/chip_ili9486.h b/democode/src/demolib/fglcd/chip_ili9486.h new file mode 100644 index 0000000..8128c65 --- /dev/null +++ b/democode/src/demolib/fglcd/chip_ili9486.h @@ -0,0 +1,85 @@ +#pragma once + +#include "chip_mipi_dcs.h" + +namespace fglcd { + +struct ILI9486_Specs : public MIPI_DCS_Specs +{ + // reset needs a bit of wait time before the screen is fully initialized + template struct ResetTriggerType : public TriggerWaitMS, 10>, 10> {}; + // safer to use a LowTrigger, but this is a tad faster + template struct DCxTriggerType : public ToggleTrigger {}; + + typedef u16 DimType; + static const DimType LONG_SIZE = 480; + static const DimType SHORT_SIZE = 320; + + enum ChipSpecific + { + power_control_1 = 0xC0, + power_control_2 = 0xC1, + power_control_3_normal_mode = 0xC2, + power_control_4_idle_mode = 0xC3, + power_control_5_partial_mode = 0xC4, + vcom_control = 0xC5, + display_function_control = 0xB6, + positive_gamma_control = 0xE0, + negative_gamma_control = 0xE1, + memory_access_control = 0x36, + }; +}; + +template +struct ILI9486 : public MIPI_DCS_Generic +{ + typedef ILI9486_Specs S; + typedef MIPI_DCS_Generic Base; + using Op = S::Opcode; + using X = S::ChipSpecific; + + static void init() + { + static const constexpr u8 tab[] PROGMEM = + { + 10, 0xF2, 0x1C, 0xA3, 0x32, 0x02, 0xb2, 0x12, 0xFF, 0x12, 0x00, + 3, 0xF1, 0x36, 0xA4, + 3, 0xF8, 0x21, 0x04, + 3, 0xF9, 0x00, 0x08, + 3, X::power_control_1, 0x0d, 0x0d, + 3, X::power_control_2, 0x43, 0x00, + 2, X::power_control_3_normal_mode, 0x00, + 3, X::vcom_control, 0x00, 0x48, + 4, X::display_function_control, 0x00, 0x22, 0x3B, + 16, X::positive_gamma_control, 0x0f, 0x24, 0x1c, 0x0a, 0x0f, 0x08, 0x43, 0x88, 0x32, 0x0f, 0x10, 0x06, 0x0f, 0x07, 0x00, + 16, X::negative_gamma_control, 0x0F, 0x38, 0x30, 0x09, 0x0f, 0x0f, 0x4e, 0x77, 0x3c, 0x07, 0x10, 0x05, 0x23, 0xb1, 0x00, + 2, X::memory_access_control, 0x0A, + 2, Op::set_address_mode, uint8_t(S::ADDRMODE_LANDSCAPE) | uint8_t(S::ADDRMODE_BGR), + 2, Op::set_pixel_format, S::PIXELFORMAT_16_BPP, + 0 + }; + + Base::init_sequence(&tab[0]); + Base::enableCS(); + enableDisplay(); + Base::disableCS(); + } + + static FORCEINLINE void enableDisplay() + { + Base::enableDisplay(); + } + + static FORCEINLINE void disableDisplay() + { + Base::disableDisplay(); + } + + // TODO: write me + static FORCEINLINE void slow_refresh() { } + static FORCEINLINE void fast_refresh() { } + + +}; + +} // end namespace fglcd diff --git a/democode/src/demolib/fglcd/chip_mipi_dcs.h b/democode/src/demolib/fglcd/chip_mipi_dcs.h new file mode 100644 index 0000000..53881a6 --- /dev/null +++ b/democode/src/demolib/fglcd/chip_mipi_dcs.h @@ -0,0 +1,300 @@ +#pragma once + +#include "chip.h" +#include "trigger.h" + +namespace fglcd { + +struct MIPI_DCS_Specs +{ + // by default, wait a bit after a reset signal until things have stabilized + template struct ResetTriggerType : public TriggerWaitMS, 10>, 10> {}; + template struct WrxCmdTriggerType : public ToggleTrigger {}; + template struct WrxDataTriggerType : public ToggleTrigger {}; + template struct DCxTriggerType : public LowTrigger {}; // ToggleTrigger would be faster but might not work, depending on hardware state + template struct CSTriggerType : public LowTrigger {}; + + // from the MIPI SPEC + enum Opcode : u8 + { + enter_idle_mode = 0x39, + enter_invert_mode = 0x21, + enter_normal_mode = 0x13, + enter_partial_mode = 0x12, + enter_sleep_mode = 0x10, + exit_idle_mode = 0x38, + exit_invert_mode = 0x20, + exit_sleep_mode = 0x11, + get_address_mode = 0x0B, + get_blue_channel = 0x08, + get_diagnostic_result = 0x0F, + get_display_mode = 0x0D, + get_green_channel = 0x07, + get_pixel_format = 0x0C, + get_power_mode = 0x0A, + get_red_channel = 0x06, + get_scanline = 0x45, + get_signal_mode = 0x0E, + nop = 0x00, + read_DDB_continue = 0xA8, + read_DDB_start = 0xA1, + read_memory_continue = 0x3E, + read_memory_start = 0x2E, + set_address_mode = 0x36, + set_column_address = 0x2A, + set_display_off = 0x28, + set_display_on = 0x29, + set_gamma_curve = 0x26, + set_page_address = 0x2B, + set_partial_columns = 0x31, + set_partial_rows = 0x30, + set_pixel_format = 0x3A, + set_scroll_area = 0x33, + set_scroll_start = 0x37, + set_tear_off = 0x34, + set_tear_on = 0x35, + set_tear_scanline = 0x44, + soft_reset = 0x01, + write_LUT = 0x2D, + write_memory_continue = 0x3C, + write_memory_start = 0x2C, + }; + + enum AddressMode : u8 // bits 5,6,7 + { + // normal mode + ADDRMODE_LR_TB = 0 << 5, + ADDRMODE_TB_LR = 1 << 5, + ADDRMODE_RL_TB = 2 << 5, + ADDRMODE_TB_RL = 3 << 5, + ADDRMODE_LR_BT = 4 << 5, + ADDRMODE_BT_LR = 5 << 5, + ADDRMODE_RL_BT = 6 << 5, + ADDRMODE_BT_RL = 7 << 5, + + ADDRMODE_LCD_REFRESH_TOP_TO_BOTTOM = (0 << 4), // default + ADDRMODE_LCD_REFRESH_BOTTOM_TO_TOP = (1 << 4), + + ADDRMODE_RGB = (0 << 3), + ADDRMODE_BGR = (1 << 3), // unconditionally set below + + ADDRMODE_FLIP_H = (1 << 1), + ADDRMODE_FLIP_V = (1 << 0), + + ADDRMODE_LANDSCAPE = ADDRMODE_TB_LR, // the default + }; + + enum PixelFormat : u8 + { + PIXELFORMAT_3_BPP = 0x11, + PIXELFORMAT_16_BPP = 0x55, + PIXELFORMAT_18_BPP = 0x66, + }; + + FORCEINLINE static constexpr u16 gencolor_inl(u8 r, u8 g, u8 b) + { + return (((r & 248) | g >> 5) << 8) | ((g & 28) << 3 | b >> 3); + } + static constexpr u16 gencolor(u8 r, u8 g, u8 b) // let the compiler decide whether to inline this + { + return gencolor_inl(r, g, b); + } + + FORCEINLINE static void splitcolor_inl(u16 col, u8 *p) + { + p[0] = uhi8(col) & 248; // 5 upper bits + p[1] = u8(col >> 3u) & 252; // 3+3 bits + p[2] = u8(col) << 3u; // 5 bits + } + NOINLINE static void splitcolor(u16 col, u8 *p) + { + splitcolor_inl(col, p); + } + +}; + +template +struct MIPI_DCS_Generic : public ChipBase +{ + typedef Specs S; + typedef ChipBase HW; + typedef typename HW::Ctrl Ctrl; + typedef typename HW::DimType DimType; + typedef typename HW::DataConnection DataConnection; + using Op = typename S::Opcode; + using AddressMode = typename S::AddressMode; + using S::gencolor; + using S::gencolor_inl; + + static void init_sequence(const u8 *tab) + { + HW::init(); + HW::reset(); + HW::enableCS(); + //disableDisplay(); + exit_sleep_mode(); + sendtable(tab); + HW::disableCS(); + //delay_ms(40); + } + + static void enableDisplay() + { + static const u8 tab[] PROGMEM = + { + 1, Op::exit_idle_mode, + 1, Op::set_display_on, + 0 + }; + sendtable(&tab[0]); + } + static void disableDisplay() + { + static const u8 tab[] PROGMEM = + { + 1, Op::enter_idle_mode, + 1, Op::set_display_off, + 0 + }; + sendtable(&tab[0]); + } + + static FORCEINLINE void soft_reset() { sendcmd(Op::soft_reset); } + static FORCEINLINE void enter_sleep_mode() { sendcmd(Op::enter_sleep_mode); } + static FORCEINLINE void exit_sleep_mode() { sendcmd(Op::exit_sleep_mode); delay_ms(5); } + static FORCEINLINE void enter_partial_mode() { sendcmd(Op::enter_partial_mode); } + static FORCEINLINE void enter_invert_mode() { sendcmd(Op::enter_invert_mode); } + static FORCEINLINE void exit_invert_mode() { sendcmd(Op::exit_invert_mode ); } + static FORCEINLINE void enter_idle_mode() { sendcmd(Op::enter_idle_mode); } + static FORCEINLINE void exit_idle_mode() { sendcmd(Op::exit_idle_mode); } + static FORCEINLINE void beginwrite() { sendcmd(Op::write_memory_start); } + static FORCEINLINE void resumewrite() { sendcmd(Op::write_memory_continue); } + static FORCEINLINE void set_tear_on(bool with_hblank = false) { sendcmd1(Op::set_tear_on, with_hblank); } + static FORCEINLINE void set_tear_off() { sendcmd(Op::set_tear_off); } + static FORCEINLINE void set_display_on() { sendcmd(Op::set_display_on); } + static FORCEINLINE void set_display_off() { sendcmd(Op::set_display_off); } + + + static constexpr DimType CONST_scroll_area_sides(DimType a, DimType b) + { + return (S::LONG_SIZE) - (a + b); + } + static NOINLINE DimType set_scroll_area_sides(DimType a, DimType b) + { + const DimType middle = CONST_scroll_area_sides(a, b); + FGLCD_ASSERT(a + b + middle == S::LONG_SIZE, "ssa"); + NoInterrupt no; + _cwru(Op::set_scroll_area, a, middle, b); + return middle; + } + + static FORCEINLINE void set_scroll_pos(DimType p) + { + FGLCD_ASSERT(int(p) >= 0 && p < S::LONG_SIZE, "ssp"); + NoInterrupt no; + _cwru(Op::set_scroll_start, p); + } + + static FORCEINLINE void set_tear_scanline(DimType scanline) + { + NoInterrupt no; + _cwru(Op::set_tear_scanline, scanline); + } + + static FORCEINLINE void set_address_mode(AddressMode mode) + { + const u8 bits = mode | S::ADDRMODE_BGR; + NoInterrupt no; + sendcmd1(Op::set_address_mode, bits); + } + + //------------------------------------- + + static NOINLINE void sendcmd(u8 com) + { + NoInterrupt no; + Ctrl::sendcmd(com); + } + static NOINLINE void sendcmd1(u8 com, u8 param) + { + NoInterrupt no; + Ctrl::sendcmd(com); + Ctrl::sendparam8(param); + } + static NOINLINE void sendcmd3(u8 com, u8 param, u8 param2, u8 param3) + { + NoInterrupt no; + Ctrl::sendcmd(com); + Ctrl::sendparam8(param); + Ctrl::sendparam8(param2); + Ctrl::sendparam8(param3); + } + template + static NOINLINE void sendtable(typename FROM::VoidPointer tab) + { + NoInterrupt no; + Ctrl::template sendtable(tab); + } + + static FORCEINLINE void _setx(DimType x1, DimType x2) + { + //FGLCD_ASSERT((int)x1 <= (int)x2 && int(x1) >= 0 && unsigned(x2) <= S::LONG_SIZE, "setx"); + _cwru(Op::set_column_address, x1, x2); + } + static FORCEINLINE void _sety(DimType y1, DimType y2) + { + //FGLCD_ASSERT((int)y1 <= (int)y2 && int(y1) >= 0 && unsigned(y2) <= S::SHORT_SIZE, "sety"); + _cwru(Op::set_page_address, y1, y2); + } + + static FORCEINLINE void setxy_inl(DimType x1, DimType y1, DimType x2, DimType y2) + { + NoInterrupt no; + _setx(x1, x2); + _sety(y1, y2); + Ctrl::sendcmd(Op::write_memory_start); + } + + static FORCEINLINE void setxywh_inl(DimType x, DimType y, DimType w, DimType h) + { + setxy(x, y, x+w-1, y+h-1); + } + + static NOINLINE void setxy(DimType x1, DimType y1, DimType x2, DimType y2) + { + setxy_inl(x1, y1, x2, y2); + } + + static NOINLINE void setxywh(DimType x, DimType y, DimType w, DimType h) + { + setxywh_inl(x,y,w,h); + } + + static FORCEINLINE void _cwru(u8 cmd, DimType a) + { + Ctrl::sendcmd(cmd); + Ctrl::sendparam16(a); + } + + static FORCEINLINE void _cwru(u8 cmd, DimType a, DimType b) + { + Ctrl::sendcmd(cmd); + Ctrl::sendparam16(a); + Ctrl::sendparam16(b); + } + + static FORCEINLINE void _cwru(u8 cmd, DimType a, DimType b, DimType c) + { + Ctrl::sendcmd(cmd); + Ctrl::sendparam16(a); + Ctrl::sendparam16(b); + Ctrl::sendparam16(c); + } +}; + +} // end namespace fglcd + +FORCEINLINE fglcd::MIPI_DCS_Specs::AddressMode operator|(fglcd::MIPI_DCS_Specs::AddressMode a, fglcd::MIPI_DCS_Specs::AddressMode b) +{ + return static_cast(uint8_t(a) | uint8_t(b)); +} diff --git a/democode/src/demolib/fglcd/chip_tm1638.h b/democode/src/demolib/fglcd/chip_tm1638.h new file mode 100644 index 0000000..f934437 --- /dev/null +++ b/democode/src/demolib/fglcd/chip_tm1638.h @@ -0,0 +1,9 @@ +#pragma once + +#include "mcu.h" + +#ifdef MCU_IS_PC +#include "chip_tm1638_emu.h" +#else +#include "chip_tm1638_hw.h" +#endif diff --git a/democode/src/demolib/fglcd/chip_tm1638_hw.h b/democode/src/demolib/fglcd/chip_tm1638_hw.h new file mode 100644 index 0000000..ba815de --- /dev/null +++ b/democode/src/demolib/fglcd/chip_tm1638_hw.h @@ -0,0 +1,229 @@ +#pragma once + +#include "mcu.h" +#include "trigger.h" +#include "lut_7seg.h" + +struct __FlashStringHelper; + +namespace fglcd { + +template +class TM1638 +{ + //static_assert(has_pin_tag::value, "StrobePin is not a pin"); + //static_assert(has_pin_tag::value, "ClockPin is not a pin"); + //static_assert(has_pin_tag::value, "DataPin is not a pin"); +public: + typedef DataPin_ DataPin; + typedef LowTrigger Clock; // was pulse + typedef LowTrigger CS; + + enum Instr : u8 + { + INSTR_DATA = 0x40, + INSTR_DISP_CTRL = 0x80, + INSTR_ADDR = 0xc0 + }; + + enum InstrData : u8 + { + INSTR_DATA_WRITE = 0x00, + INSTR_DATA_READKEYS = 0x02, + + INSTR_DATA_AUTOADD = 0x00, + INSTR_DATA_FIXED = 0x04, + + INSTR_DATA_TESTMODE = 0x08, + }; + + static void init() + { + Clock::Pin::makeOutput(); + Clock::clear(); + CS::Pin::makeOutput(); + CS::clear(); + _writemode(); + } + + static void commit() + { + // everything goes directly to hardware, nothing to do + } + + template + static void setText(const char *s, u8 beg = 0) + { + u8 c, i = 0, w = beg; + for( ; w < 8 && (c = FROM::template read(&s[i])); ++w, ++i) + { + //setChar(i, c); + c = seg7::transform(c); + if(FROM::template read(&s[i+1]) == '.') + { + c |= 0x80; + ++i; + } + setRaw(w, c); + } + } + static void setText(const __FlashStringHelper *s, u8 beg = 0) + { + setText(reinterpret_cast(s), beg); + } + + static void update(const u8 *p, u8 n, u8 ledr, u8 ledg = 0, u8 beg = 0) + { + NoInterrupt no; + _instr_data(INSTR_DATA_WRITE | INSTR_DATA_AUTOADD); + CS::set(); + _send_addr(beg << 1); + for(u8 i = 0; i < n; ++i, ledr >>= 1, ledg >>= 1) + { + _send(p[i]); + _send((ledr & 1) | ((ledg & 1) << 1)); + } + CS::clear(); + } + + static void intensity(u8 x) // 0: off, 1: low .. 8: bright + { + if(x) + { + --x; + if(x > 7) + x = 7; + x |= 8; // display on bit + } + + NoInterrupt no; + _instr_disp(x); + + // necessary for the TM1640 + CS::set(); + Clock::trigger(); + CS::clear(); + } + + static void setLED(u8 pos, u8 color) + { + NoInterrupt no; + _instr_data(INSTR_DATA_WRITE | INSTR_DATA_FIXED); + CS::set(); + _send_addr((pos << 1) | 1); + _send(color); + CS::clear(); + } + + static void setLEDs(u8 r, u8 g = 0) + { + for(u8 i = 0; i < 8; ++i, r >>= 1, g >>= 1) + { + u8 col = (r & 1) | ((g & 1) << 1); + setLED(i, col); + } + } + + static u8 getButtons() + { + u8 keys = 0; + NoInterrupt no; + CS::set(); + _send(INSTR_DATA | INSTR_DATA_READKEYS | INSTR_DATA_AUTOADD); + _readmode(); + for (u8 i = 0; i < 4; i++) + { + delay_us(1); + keys |= _read() << i; + } + CS::clear(); + _writemode(); + + return keys; + } + + static void clear() + { + NoInterrupt no; + _instr_data(INSTR_DATA_WRITE | INSTR_DATA_AUTOADD); + CS::set(); + _send_addr(0); + for(u8 i = 0; i < 16; ++i) + _send(0); + CS::clear(); + } + + static void setRaw(u8 pos, u8 data) + { + _senddata(pos << 1, data); + } + static void setChar(u8 pos, u8 data) + { + setRaw(pos, seg7::transform(data)); + } + +private: + + static FORCEINLINE void _instr_data(u8 d) + { + _sendcmd(INSTR_DATA | d); + } + static FORCEINLINE void _send_addr(u8 addr) + { + _send(INSTR_ADDR | addr); + } + static FORCEINLINE void _instr_disp(u8 d) + { + _sendcmd(INSTR_DISP_CTRL | d); + } + + static void _send(u8 x) + { + for (u8 i = 0; i < 8; i++, x >>= 1) + { + DataPin::set(x & 1); + Clock::trigger(); + } + } + static FORCEINLINE void _sendcmd(u8 cmd) + { + CS::set(); + _send(cmd); + CS::clear(); + } + static FORCEINLINE void _senddata(u8 addr, u8 data) + { + _instr_data(INSTR_DATA_WRITE | INSTR_DATA_FIXED); + CS::set(); + _send_addr(addr); + _send(data); + CS::clear(); + } + + static FORCEINLINE void _writemode() + { + DataPin::makeOutput(); + DataPin::lo(); + } + static FORCEINLINE void _readmode() + { + DataPin::makeInput(); + DataPin::hi(); // enable pullup + } + + static FORCEINLINE u8 _read() + { + u8 x = 0; + for(u8 i = 0; i < 8; ++i) + { + Clock::trigger(); + x >>= 1; + if(DataPin::get()) + x |= 0x80; + } + return x; + } +}; + + +} // end namespace fglcd diff --git a/democode/src/demolib/fglcd/con_base.h b/democode/src/demolib/fglcd/con_base.h new file mode 100644 index 0000000..233bbed --- /dev/null +++ b/democode/src/demolib/fglcd/con_base.h @@ -0,0 +1,92 @@ +#pragma once + +#include "trigger.h" + +namespace fglcd { + +// The actual connection. Con is used to set data, trigger is fired to notify external hardware that new data were set. +template +struct Connection +{ + typedef bool is_connection_tag; + typedef TRIGGER Trigger; + static_assert(has_con_tag::value, "Con is not a con"); + static_assert(has_trigger_tag::value, "Trigger is not a trigger"); + typedef typename Con::value_type value_type; + + static FORCEINLINE void _latch() { Trigger::trigger(); } + static FORCEINLINE void send(value_type v) { Con::send(v); _latch(); } + static FORCEINLINE void send() { Con::send(); _latch(); } + static FORCEINLINE void set(value_type v) { Con::set(v); } + static FORCEINLINE void sendSameAgain(value_type v) { Con::sendSameAgain(v); _latch(); } + static FORCEINLINE value_type readOutput() { return Con::readOutput(); } +}; + +// Connection config: Send data via a (parallel) port, define (but not used in this class) a trigger. +// (The trigger is further specialized by the Chip<> class -- hold times and so on) +template +struct Con_Port +{ + typedef bool is_con_tag; + typedef PORT Port; + typedef TRIGGERPIN TriggerPin; // Used by Connection<> + static_assert(has_port_tag::value, "Port is not a port"); + static_assert(has_pin_tag::value, "TriggerPin is not a pin"); + typedef typename Port::value_type value_type; + + static FORCEINLINE void set(value_type v) { Port::set(v); } + static FORCEINLINE void send(value_type v) { Port::set(v); } + static FORCEINLINE void send() {} // nothing to do + static FORCEINLINE void sendSameAgain(value_type) {} // nothing to do + static FORCEINLINE value_type readOutput() { return Port::readOutput(); } +}; + +enum SPIConWaitSendOrder +{ + SPI_CON_WAIT_THEN_SEND, + SPI_CON_SEND_THEN_WAIT, + SPI_CON_SEND +}; + +/* +template struct SPISender; +template<> struct SPISender { FORCEINLINE void send(unative x) { SPI::waitSend(x); } }; +template<> struct SPISender { FORCEINLINE void send(unative x) { SPI::sendWait(x); } }; +template<> struct SPISender { FORCEINLINE void send(unative x) { SPI::send(x); } }; + +// SPI connection +template +struct Con_SPI; + +template +struct Con_SPI +{ + typedef bool is_con_tag; + typedef u8 value_type; + typedef DummyPin TriggerPin; // Used by Connection<> + typedef SPISender Send; + static value_type _last; + static FORCEINLINE void set(value_type v) { _last = v; } + static FORCEINLINE void send() { send(_last); } + static FORCEINLINE void send(value_type v) { Send::send(v); } + static FORCEINLINE void sendSameAgain(value_type v) { send(v); } + static FORCEINLINE value_type readOutput() { return _last; } +}; + +template +struct Con_SPI +{ + typedef bool is_con_tag; + typedef u16 value_type; + typedef DummyTrigger TriggerPin; // Used by Connection<> + typedef SPISender Send; + static value_type _last; + static FORCEINLINE void set(value_type v) { _last = v; } + static FORCEINLINE void send() { send(_last); } + static FORCEINLINE void send(value_type v) { Send::send(ulo8(v)); Send::send(uhi8(v)); } + static FORCEINLINE void sendSameAgain(value_type v) { send(v); } + static FORCEINLINE value_type readOutput() { return _last; } +}; +*/ + +} // end namespace fglcd diff --git a/democode/src/demolib/fglcd/def.h b/democode/src/demolib/fglcd/def.h new file mode 100644 index 0000000..0c6482e --- /dev/null +++ b/democode/src/demolib/fglcd/def.h @@ -0,0 +1,93 @@ +#pragma once + +// This is THE main debug switch. +// Comment out to kill ALL the asserts, checks, buttonpanel references, and whatnot. +#ifndef __AVR__ +#define FGLCD_DEBUG +#endif + +// And here goes ALL inline assembly +#define FGLCD_ASM + + +#ifdef _CHECK_PRAGMA_ONCE +#error pragma once disobeyed! wtf, gcc! +#else +#define _CHECK_PRAGMA_ONCE +#endif + +#ifdef __GNUC__ +#pragma GCC optimize ("O3") +#define FORCEINLINE __attribute__((always_inline)) inline +#define NOINLINE __attribute__((noinline)) +#define ALIGN(x) __attribute__((aligned(x))) +#endif + +#ifdef _MSC_VER +#define FORCEINLINE __forceinline +#define NOINLINE __declspec(noinline) +#define ALIGN(x) __declspec(align(x)) +#endif + +#include +#include +#include + + + +template +struct ALIGN(A) AlignedMem +{ + uint8_t mem[SZ]; +}; + +namespace fglcd { + +namespace types { + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int32_t s64; + +} + +using namespace types; + +struct NoInstance +{ +private: + NoInstance(); +}; + +namespace priv { + +template +struct IntegralConstant +{ + typedef T value_type; + typedef IntegralConstant type; + enum { value = v }; +}; + +typedef IntegralConstant CompileTrue; +typedef IntegralConstant CompileFalse; +template struct CompileCheck : IntegralConstant{}; + +} // end namespace priv + +template struct is_same : priv::CompileFalse { }; +template struct is_same : priv::CompileTrue { }; + +template struct TypeSwitch{}; +template struct TypeSwitch { typedef A type; }; +template struct TypeSwitch { typedef B type; }; + + + +} // end namespace fglcd diff --git a/democode/src/demolib/fglcd/fglcd.h b/democode/src/demolib/fglcd/fglcd.h new file mode 100644 index 0000000..14bd53d --- /dev/null +++ b/democode/src/demolib/fglcd/fglcd.h @@ -0,0 +1,6 @@ +#pragma once + +#include "lcd.h" +#include "presets.h" +#include "chip_tm1638.h" +#include "overrides.h" diff --git a/democode/src/demolib/fglcd/fgterm.h b/democode/src/demolib/fglcd/fgterm.h new file mode 100644 index 0000000..bec8dd9 --- /dev/null +++ b/democode/src/demolib/fglcd/fgterm.h @@ -0,0 +1,243 @@ +#pragma once + +// See http://overskill.alexshu.com/128x64-12864-lcd-w-arduino-serial/ for wiring + + +// Define exactly one of these before including +//#define FGTERM_USE_128x64 +//#define FGTERM_USE_SERIAL +//#define FGTERM_USE_2004 + +#ifdef FGTERM_USE_2004 + #include + #include +#elif defined(FGTERM_USE_128x64) + #include "U8glib.h" + /*#include + #ifdef U8X8_HAVE_HW_SPI + #include + #endif + #ifdef U8X8_HAVE_HW_I2C + #include + #endif*/ +#else +# error Which LCD? +#endif + +namespace fgterm { + +#ifdef FGTERM_USE_2004 + #include + static const u8 LCD_ADDR = 0x3f; + static const u8 LCD_INFO_LINELEN = 20; + static const u8 LCD_INFO_NLINES = 4; + static LiquidCrystal_PCF8574 lcd(LCD_ADDR); +#elif defined(FGTERM_USE_128x64) + static const u8 PIN_LCD_CS = 9; + static const u8 PIN_LCD_RESET = 8; // or U8G_PIN_NONE + //U8G2_ST7920_128X64_1_HW_SPI lcd(U8G2_R0, /* CS=*/ 12, /* reset=*/ 10); + U8GLIB_ST7920_128X64 lcd(PIN_LCD_CS, U8G_PIN_NONE, PIN_LCD_RESET); // use hardware SPI + //U8GLIB_ST7920_128X64 lcd(13, 11, 12, U8G_PIN_NONE, 8); // software SPI + static const u8 LCD_INFO_LINELEN = 32; + static const u8 LCD_INFO_NLINES = 10; + static const u8 LCD_LINE_HEIGHT = 6; +#else +#error wat +#endif + +static char lcdbuf[LCD_INFO_NLINES][LCD_INFO_LINELEN+1]; +static u8 curbufline = 0, curbufcol = 0; +static u8 wline = 0; + + + +static char * _lcdnextline() +{ + ++curbufline; + curbufline %= LCD_INFO_NLINES; + if(wline < LCD_INFO_NLINES) + ++wline; + return lcdbuf[curbufline]; +} + +static void _lcdfinishprint(char *p, u8 written) +{ + if(const u8 remain = LCD_INFO_LINELEN - written) + memset(p + written, ' ', remain); +} + +static void _lcdprint(const char *p, u8 lcdline) +{ +#ifdef FGTERM_USE_2004 + lcd.setCursor(0, lcdline); + lcd.print(p); +#elif defined(FGTERM_USE_128x64) + //lcd.setPrintPos(0, lcdline * LCD_LINE_HEIGHT); + //lcd.print(p); + + //digitalWrite(PIN_LCD_CS, HIGH); + lcd.drawStr( 0, (lcdline * LCD_LINE_HEIGHT) + (LCD_LINE_HEIGHT-1), p); + //digitalWrite(PIN_LCD_CS, LOW); +#else +#error wat +#endif +} + +static void _lcdprintline(u8 bufline, u8 lcdline) +{ + const char *p = lcdbuf[bufline]; + _lcdprint(p, lcdline); +} + +static void _lcdflush2() +{ + for(u8 i = 0; i < LCD_INFO_NLINES; ++i) + _lcdprintline((curbufline + i) % LCD_INFO_NLINES, (wline + i) % LCD_INFO_NLINES); +} + +static void lcdflush() +{ +#ifdef FGTERM_USE_128x64 + //digitalWrite(PIN_LCD_CS, HIGH); + lcd.firstPage(); + do + _lcdflush2(); + while(lcd.nextPage()); + //digitalWrite(PIN_LCD_CS, LOW); +#else + _lcdflush2(); +#endif +} + +static void lcdclear() +{ + memset(lcdbuf, ' ', sizeof(lcdbuf)); + for(u8 i = 0; i < LCD_INFO_NLINES; ++i) + lcdbuf[i][LCD_INFO_LINELEN] = 0; + wline = 0; + curbufline = 0; + curbufcol = 0; +#ifdef FGTERM_USE_2004 + lcd.home(); + lcd.clear(); +#elif defined(FGTERM_USE_128x64) + lcd.setColorIndex(0); + lcd.drawBox( 0, 0, lcd.getWidth(), lcd.getHeight() ); + lcd.setColorIndex(1); + //lcd.setPrintPos(0, 0); +#else +#error wat +#endif +} + + +template +struct ReadChar {}; + +template<> struct ReadChar { inline static char get(const void *p) { return pgm_read_byte(p); } }; +template<> struct ReadChar { inline static char get(const void *p) { return *(char*)p; } }; + +inline static char readChar(const char *s, u8 offs) { return ReadChar::get(s + offs); } +inline static char readChar(const __FlashStringHelper *s, u8 offs) { return ReadChar::get(((const char*)s) + offs); } + + +template +struct LcdPrint +{ + static void print(const TY *s) + { + char *p = lcdbuf[curbufline]; + char c; + u8 n = curbufcol; + for(u8 i = 0; ((c = readChar(s, i))); ++i) + { + if(c == '\n') + goto fin; + + p[n++] = c; + + if(n >= LCD_INFO_LINELEN) + { + fin: + _lcdfinishprint(p, n); + n = 0; + p = _lcdnextline(); + } + } + curbufcol = n; + } +}; + + +static void print(const char *s, bool flush = true) +{ +#ifdef FGTERM_USE_SERIAL + Serial.print(s); +#endif + LcdPrint::print(s); + if(flush) + lcdflush(); +} +static void print(const __FlashStringHelper *s, bool flush = true) +{ +#ifdef FGTERM_USE_SERIAL + Serial.print(s); +#endif + LcdPrint<__FlashStringHelper>::print(s); + if(flush) + lcdflush(); +} + +static void newline(bool flush = true) +{ + print(F("\n"), flush); +} + +static void printml(const char *buf, unsigned length) +{ + const bool full = length && length % LCD_INFO_LINELEN == 0; + print(buf, full); + if(!full) + fgterm::newline(); +#ifdef FGTERM_USE_SERIAL + Serial.println(); +#endif +} + +static void printml(const char *buf) +{ + return printml(buf, strlen(buf)); +} + +void initLCD() +{ +#ifdef FGTERM_USE_2004 + Wire.begin(); +#ifdef FGTERM_USE_SERIAL + Wire.beginTransmission(LCD_ADDR); + u8 error = Wire.endTransmission(); + if (error) + Serial.println(F("NO LCD FOUND")); +#endif + lcd.begin(LCD_INFO_LINELEN, LCD_INFO_NLINES); + lcd.setBacklight(50); + +#elif defined(FGTERM_USE_128x64) + lcd.begin(); + lcd.setColorIndex(1); + lcd.setFont(u8g_font_4x6); + //lcd.setRot180(); +#endif + + /*for(u8 i = 0; ; i += 8) + { + char buf[64]; + sprintf(buf, "0123456789abcdef0123456789ab+%d", i); + printml(buf); + }*/ + + lcdclear(); +} + + +}; diff --git a/democode/src/demolib/fglcd/interface.h b/democode/src/demolib/fglcd/interface.h new file mode 100644 index 0000000..4b65a35 --- /dev/null +++ b/democode/src/demolib/fglcd/interface.h @@ -0,0 +1,35 @@ +#pragma once + +#include "con_base.h" + +namespace fglcd { + +struct NoHwInit +{ + static FORCEINLINE void init() {} +}; + +template +struct Interface +{ + typedef TDCxPin DCxPin; + typedef TCSPin CSPin; + typedef TResetPin ResetPin; + typedef TCmdCon CmdCon; + typedef TDataCon DataCon; + typedef THwInit HwInit; + + static FORCEINLINE void init() + { + HwInit::init(); + } + + static_assert(has_con_tag::value, "CmdCon is not a con"); + static_assert(has_con_tag::value, "DataCon is not a con"); + static_assert(has_pin_tag::value, "DCxPin is not a pin"); + static_assert(has_pin_tag::value, "CSPin is not a pin"); + static_assert(has_pin_tag::value, "ResetPin is not a pin"); +}; + + +} // end namespace fglcd diff --git a/democode/src/demolib/fglcd/lcd.h b/democode/src/demolib/fglcd/lcd.h new file mode 100644 index 0000000..774fbc0 --- /dev/null +++ b/democode/src/demolib/fglcd/lcd.h @@ -0,0 +1,107 @@ +#pragma once + +#include "chip.h" + +namespace fglcd { + +template +struct _LcdBase : public CHIPX +{ + typedef CHIPX Chip; + typedef typename Chip::Specs Specs; + typedef typename Chip::DataConnection DataConnection; + typedef typename Chip::DimType DimType; + typedef typename Chip::TotalPixelType TotalPixelType; + typedef typename DataConnection::value_type ColorType; + + struct PixelPos + { + PixelPos() {} + PixelPos(DimType a, DimType b) : x(a), y(b) {} + DimType x, y; + }; + + // LCD commands modify PORTC and therefore leave garbage on the port, + // therefore backup the color first, then change stuff, then put the color back + struct StateBackup : private InsideInterrupt + { + const ColorType color; + FORCEINLINE StateBackup() + : color(getCurrentColor()) + {} + + ~StateBackup() + { + Chip::resumewrite(); + setColor(color); + } + }; + + static const DimType WIDTH = Specs::LONG_SIZE; // TODO: use template for landscape/orientation selection themode + static const DimType HEIGHT = Specs::SHORT_SIZE; + static const DimType XMAX = Specs::LONG_SIZE - 1; // TODO: use template for landscape/orientation selection themode + static const DimType YMAX = Specs::SHORT_SIZE - 1; + static FORCEINLINE ColorType getCurrentColor() { return DataConnection::readOutput(); } + static FORCEINLINE void setColor(ColorType c) { DataConnection::set(c); } + static FORCEINLINE void sendPixel() { DataConnection::send(); } + static FORCEINLINE void sendPixel(ColorType c) { DataConnection::send(c); } + static FORCEINLINE void sendPixelAgain(ColorType c) { DataConnection::sendSameAgain(c); } + + static FORCEINLINE void preparefill() + { + // Forcing inlining here actually reduces code size + Chip::setxy_inl(0, 0, XMAX, YMAX); + } + + static FORCEINLINE void fastfill_inl(TotalPixelType npix) + { + static constexpr TotalPixelType PIXELS_PER_ROUND = Chip::TOTAL_PIXELS / TotalPixelType(32); // unroll factor + typedef typename TypeForSize::type IterType; + FGLCD_DUFF32_DIV(IterType, npix, sendPixel() ); + } + + static NOINLINE void fastfill_huge(TotalPixelType npix) + { + fastfill_inl(npix); + } + + static NOINLINE void fastfill_u8(u8 npix) + { + FGLCD_DUFF16(u8, npix, sendPixel() ); + } + + static NOINLINE void fastfill_u16(u16 npix) + { + FGLCD_DUFF32(u16, npix, sendPixel() ); + } + + static NOINLINE void clear(ColorType color) + { + preparefill(); + setColor(color); + fastfill_inl(Chip::TOTAL_PIXELS); // Forcing inline helps the compiler kill most of the duff device + } + + static void fillrect(DimType x, DimType y, DimType w, DimType h, ColorType color) + { + Chip::setxywh(x, y, w, h); + setColor(color); + const TotalPixelType total = TotalPixelType(w) * h; + fastfill_huge(total); + } + + static void fillrectfromto(DimType x, DimType y, DimType x1, DimType y1, ColorType color) + { + Chip::setxy(x,y,x1, y1); + setColor(color); + const TotalPixelType total = TotalPixelType(x1-x+1)*TotalPixelType(y1-y+1); + fastfill_huge(total); + } +}; + +template class CHIP, typename Iface> +struct LcdBase : public _LcdBase > +{ +}; + +} // end namespace fglcd diff --git a/democode/src/demolib/fglcd/lut_7seg.h b/democode/src/demolib/fglcd/lut_7seg.h new file mode 100644 index 0000000..c8ec791 --- /dev/null +++ b/democode/src/demolib/fglcd/lut_7seg.h @@ -0,0 +1,91 @@ +#pragma once + +#include "mcu.h" + +namespace fglcd { +namespace seg7 { + + +enum NBit +{ + U = 1, + UR = 2, + BR = 4, + B = 8, + BL = 16, + UL = 32, + M = 64, + DOT = 128, + ALLN = 0xff - DOT +}; + +static const u8 D_num[] PROGMEM = +{ + /* 0 */ ALLN - M, + /* 1 */ UR|BR, + /* 2 */ U|UR|M|BL|B, + /* 3 */ U|UR|M|BR|B, + /* 4 */ UL|M|UR|BR, + /* 5 */ U|UL|M|BR|B, + /* 6 */ ALLN - UR, + /* 7 */ U|UR|BR, + /* 8 */ ALLN, + /* 9 */ ALLN - BL +}; + +static const u8 D_al[] PROGMEM = +{ + /* a */ ALLN - B, + /* b */ ALLN - U - UR, + /* c */ ALLN - UR-M-BR, + /* d */ ALLN - U-UL, + /* e */ ALLN - UR-BR, + /* f */ U|UL|M|BL, + /* g */ ALLN - UR-M, + /* h */ ALLN - U-B, + /* i */ BL | U, + /* j */ B|BR|UR|BL, + /* k */ UL|BL|M|UR|B, // sucks + /* l */ UL|BL|B, + /* m */ ALLN - B-M, // sucks + /* n */ BL|M|BR, + /* o */ M|B|BL|BR, + /* p */ ALLN - B-BR, + /* q */ ALLN - BL-B, + /* r */ BL|M, + /* s */ ALLN-UR-BL, + /* t */ UL|M|BL|B, + /* u */ ALLN - U-M, + /* v */ BL|B|BR, + /* w */ ALLN - U, // sucks + /* x */ UR|M|BL, // sucks + /* y */ ALLN - U-BL, + /* z */ ALLN - UL-BR + DOT, +}; + +static const u8 D_specialLUT[] PROGMEM = { ' ', '_', '=', '|', '^', '-', ',', '/', '\\', '\'', '.', ']', '[', ')', '(' }; +static const u8 D_special[] PROGMEM = { 0, B, B|M, BR|BL, UL|U|UR, M, BR|B, UR|M|BL, UL|M|BR, UR, DOT, U|UR|BR|B, U|UL|BL|B, U|UR|BR|B, U|UL|BL|B }; + +static u8 transform(u8 c) +{ + if(c >= '0' && c <= '9') + return memtype::Progmem::read(&D_num[c - '0']); + if(c <= 9) + return memtype::Progmem::read(&D_num[c]); + if(c <= 0xf) + return memtype::Progmem::read(&D_al[c]); + if(c >= 'a' && c <= 'z') + return memtype::Progmem::read(&D_al[c - 'a']); + if(c >= 'A' && c <= 'Z') + return memtype::Progmem::read(&D_al[c - 'A']); + + for(u8 i = 0; i < sizeof(D_specialLUT); ++i) + if(c == memtype::Progmem::read(&D_specialLUT[i])) + return memtype::Progmem::read(&D_special[i]); + + return U|M|B; +} + + +} +} diff --git a/democode/src/demolib/fglcd/macros.h b/democode/src/demolib/fglcd/macros.h new file mode 100644 index 0000000..dfc23aa --- /dev/null +++ b/democode/src/demolib/fglcd/macros.h @@ -0,0 +1,172 @@ +#pragma once + +#include "def.h" + +#if defined(FGLCD_DEBUG) +#define FGLCD_ASSERT_VAL2(cond, msg, val, val2) do { if(!(cond)) { ::_assert_fail(PSTR(#cond), PSTR(msg), __LINE__, val, val2); } } while(0) +#define FGLCD_ASSERT_VAL(cond, msg, val) FGLCD_ASSERT_VAL2(cond, msg, val, val); +#define FGLCD_ASSERT(cond, msg) FGLCD_ASSERT_VAL(cond, msg, -1337); +#elif defined(_DEBUG) +#include +#define FGLCD_ASSERT(cond, msg) assert((cond) && msg) +#define FGLCD_ASSERT_VAL(cond, msg, val) assert((cond) && msg) +#define FGLCD_ASSERT_VAL2(cond, msg, val, val2) assert((cond) && msg) +#else +#define FGLCD_ASSERT(cond, msg) +#define FGLCD_ASSERT_VAL(cond, msg, val) +#define FGLCD_ASSERT_VAL2(cond, msg, val, val2) +#endif + +#define FGLCD_REP_2(code) do { code; code; } while(0) +#define FGLCD_REP_3(code) do { code; code; code; } while(0) +#define FGLCD_REP_4(code) do { code; code; code; code; } while(0) +#define FGLCD_REP_5(code) do { code; code; code; code; code; } while(0) +#define FGLCD_REP_6(code) do { code; code; code; code; code; code; } while(0) +#define FGLCD_REP_8(code) do { FGLCD_REP_4(code); FGLCD_REP_4(code); } while(0) +#define FGLCD_REP_16(code) do { FGLCD_REP_8(code); FGLCD_REP_8(code); } while(0) +#define FGLCD_REP_32(code) do { FGLCD_REP_16(code); FGLCD_REP_16(code); } while(0) + +#if 0 + +// old version -- generates jump table and usually larger code and somtimes spurious/unnecessary movs + +#define FGLCD_DUFF4(T, N, code) \ + do { \ + T n = (T)(((N) + 3u) / 4u); \ + switch ((N) % 4u) { \ + case 0: do { code; \ + case 3: code; \ + case 2: code; \ + case 1: code; \ + } while (--n); \ + }} while(0) + +#define FGLCD_DUFF8(T, N, code) \ + do { \ + T n = (T)(((N) + 7u) / 8u); \ + switch ((N) % 8u) { \ + case 0: do { code; \ + case 7: code; \ + case 6: code; \ + case 5: code; \ + case 4: code; \ + case 3: code; \ + case 2: code; \ + case 1: code; \ + } while (--n); \ + }} while(0) + +#define FGLCD_DUFF16(T, N, code) \ + do { \ + T n = (T)(((N) + 15u) / 16u); \ + switch ((N) % 16u) { \ + case 0: do { code; \ + case 15: code; \ + case 14: code; \ + case 13: code; \ + case 12: code; \ + case 11: code; \ + case 10: code; \ + case 9: code; \ + case 8: code; \ + case 7: code; \ + case 6: code; \ + case 5: code; \ + case 4: code; \ + case 3: code; \ + case 2: code; \ + case 1: code; \ + } while (--n); \ + }} while(0) + +#define FGLCD_DUFF32(T, N, code) \ + do { \ + T n = (T)(((N) + 31u) / 32u); \ + switch ((N) % 32u) { \ + case 0: do { code; \ + case 31: code; \ + case 30: code; \ + case 29: code; \ + case 28: code; \ + case 27: code; \ + case 26: code; \ + case 25: code; \ + case 24: code; \ + case 23: code; \ + case 22: code; \ + case 21: code; \ + case 20: code; \ + case 19: code; \ + case 18: code; \ + case 17: code; \ + case 16: code; \ + case 15: code; \ + case 14: code; \ + case 13: code; \ + case 12: code; \ + case 11: code; \ + case 10: code; \ + case 9: code; \ + case 8: code; \ + case 7: code; \ + case 6: code; \ + case 5: code; \ + case 4: code; \ + case 3: code; \ + case 2: code; \ + case 1: code; \ + } while (--n); \ + }} while(0) + +#else + +// new version -- generates much better code +// also renamed internal vars to make sure there are no name conflicts; +// as otherwise the gcc optimizer acts weird and doesn't actually unroll...? + +#define FGLCD_DUFF4(T, N, code) \ + do { \ + uint8_t _DUFF_r = uint8_t((N) & 3u); \ + while(_DUFF_r--) { code; } \ + T _DUFF_n = (T)((N) & ~3u); \ + while(_DUFF_n) { FGLCD_REP_4(code); _DUFF_n -= 4; } \ + } while(0) + + #define FGLCD_DUFF8(T, N, code) \ + do { \ + uint8_t _DUFF_r = uint8_t((N) & 7u); \ + while(_DUFF_r--) { code; } \ + T _DUFF_n = (T)((N) & ~7u); \ + while(_DUFF_n) { FGLCD_REP_8(code); _DUFF_n -= 8; } \ + } while(0) + + #define FGLCD_DUFF16(T, N, code) \ + do { \ + uint8_t _DUFF_r = uint8_t((N) & 15u); \ + while(_DUFF_r--) { code; } \ + T _DUFF_n = (T)((N) & ~15u); \ + while(_DUFF_n) { FGLCD_REP_16(code); _DUFF_n -= 16; } \ + } while(0) + + #define FGLCD_DUFF32(T, N, code) \ + do { \ + uint8_t _DUFF_r = uint8_t(uint8_t(N) & 3u); \ + while(_DUFF_r--) { code; } \ + _DUFF_r = uint8_t(uint8_t(N) & (31u & ~3u)); \ + while(_DUFF_r) { FGLCD_REP_4(code); _DUFF_r -= 4; } \ + T _DUFF_n = (T)((N) & ~31u); \ + while(_DUFF_n) { FGLCD_REP_32(code); _DUFF_n -= 32; } \ + } while(0) + + + #define FGLCD_DUFF32_DIV(T, N, code) \ + do { \ + uint8_t _DUFF_r = uint8_t(uint8_t(N) % 32u); \ + while(_DUFF_r--) { code; } \ + _DUFF_r = uint8_t(uint8_t(N) & (31u & ~3u)); \ + while(_DUFF_r) { FGLCD_REP_4(code); _DUFF_r -= 4; } \ + T _DUFF_n = (T)((N) / 32u); \ + while(_DUFF_n--) { FGLCD_REP_32(code); } \ + } while(0) + +#endif diff --git a/democode/src/demolib/fglcd/mcu.h b/democode/src/demolib/fglcd/mcu.h new file mode 100644 index 0000000..75dd306 --- /dev/null +++ b/democode/src/demolib/fglcd/mcu.h @@ -0,0 +1,199 @@ +#pragma once + +#include "def.h" +#include "macros.h" + + +// functions for software port emulation +extern void mcu_out_port_write(unsigned portnum, unsigned value); +extern unsigned mcu_out_port_read(unsigned portnum); +extern void mcu_in_port_write(unsigned portnum, unsigned value); +extern unsigned mcu_in_port_read(unsigned portnum); +extern void mcu_ddr_port_write(unsigned portnum, unsigned value); +extern unsigned mcu_ddr_port_read(unsigned portnum); + +namespace fglcd { + +template +struct HardwareMMIO +{ + typedef TY value_type; + + // this doesn't work due to gcc 6+ bug + // ref: https://stackoverflow.com/questions/24398102/constexpr-and-initialization-of-a-static-const-void-pointer-with-reinterpret-cas + /*static constexpr const _hwWrite = (volatile TY*)aWP; + static constexpr const _hwRead = (volatile TY*)aRP; + static constexpr const _hwDDR = (volatile TY*)aDDR;*/ + + // workaround + static constexpr const uintptr_t _hwWrite = aWP; + static constexpr const uintptr_t _hwRead = aRP; + static constexpr const uintptr_t _hwDDR = aDDR; + static FORCEINLINE volatile TY *hwRead() { return reinterpret_cast(_hwRead); } + static FORCEINLINE volatile TY *hwWrite() { return reinterpret_cast(_hwWrite); } + static FORCEINLINE volatile TY *hwDDR() { return reinterpret_cast(_hwDDR); } + + static FORCEINLINE void _writeOut(TY v) { *hwWrite() = v; } + static FORCEINLINE TY _readOut() { return *hwWrite(); } + static FORCEINLINE void _writeIn(TY v) { *hwRead() = v; } + static FORCEINLINE TY _readIn() { return *hwRead(); } + static FORCEINLINE void _writeDDR(TY v) { *hwDDR() = v; } + static FORCEINLINE TY _readDDR() { return *hwDDR(); } +}; + +template +struct EmulatedMMIO +{ + typedef TY value_type; + + static FORCEINLINE void _writeOut(value_type v) { mcu_out_port_write(portnum, v); } + static FORCEINLINE value_type _readOut() { return (value_type)mcu_out_port_read(portnum); } + static FORCEINLINE void _writeIn(value_type v) { mcu_in_port_write(portnum, v); } + static FORCEINLINE value_type _readIn() { return (value_type)mcu_in_port_read(portnum); } + static FORCEINLINE void _writeDDR(value_type v) { mcu_ddr_port_write(portnum, v); } + static FORCEINLINE value_type _readDDR() { return (value_type)mcu_ddr_port_read(portnum); } +}; + +template +struct PortBase +{ + typedef bool is_port_tag; + typedef IO_ IO; + typedef typename IO::value_type value_type; + + static FORCEINLINE void set(value_type v) { IO::_writeOut(v); } + static FORCEINLINE value_type get() { return IO::_readIn(); } + static FORCEINLINE value_type readOutput() { return IO::_readOut(); } + static FORCEINLINE void makeOutput(value_type ddr = ~value_type(0)) { IO::_writeDDR(ddr); } + static FORCEINLINE void makeInput(value_type ddr = ~value_type(0)) { makeOutput(~ddr); } + static FORCEINLINE value_type isOutput() { return IO::_readDDR(); } + static FORCEINLINE value_type isInput() { return ~isOutput(); } + static FORCEINLINE void toggle(value_type v) { set(get() ^ v); } + + template + static FORCEINLINE void setbit() { set(get() | value_type(bit << 1)); } + template + static FORCEINLINE void clearbit() { set(get() & ~value_type(bit << 1)); } +}; + +/* +template +struct HardwarePort : public PortBase > +{ + typedef HardwareMMIO MMIO; + typedef PortBase Base; +}; +*/ + + +template +struct PinBase +{ + typedef bool is_pin_tag; + typedef PORT Port; + typedef typename PORT::value_type value_type; + enum { mask = 1 << bit }; + + static FORCEINLINE void hi() { Port::template setbit(); } + static FORCEINLINE void lo() { Port::template clearbit(); } + //static FORCEINLINE void set(bool v) { if(get() != v) toggle(); } + static FORCEINLINE void set(bool v) { if(v) hi(); else lo(); } // safer + static FORCEINLINE bool get() { return !!(Port::get() & mask); } + static FORCEINLINE void toggle() { Port::toggle(mask); } + static FORCEINLINE void pulse_lo() { lo(); hi(); } + static FORCEINLINE void pulse_hi() { hi(); lo(); } + static FORCEINLINE void pulse() { toggle(); toggle(); } + static FORCEINLINE void makeOutput(bool out = true) { Port::makeOutput(Port::isOutput() | (value_type(out) << bit)); } + static FORCEINLINE void makeInput(bool in = true) { makeOutput(!in); } +}; + +} // end namespace fglcd + +#ifdef __AVR__ +#include "mcu_avr.h" +#elif defined(_M_IX86) || defined(_WIN32) || defined(unix) +#include "mcu_pc.h" +#else +#error unknown mcu +#endif + +namespace fglcd { + +using memtype::RAM; +using memtype::Progmem; +using memtype::ProgmemFar; + +template +struct DummyPortBase +{ + typedef bool is_port_tag; + typedef TY value_type; + static FORCEINLINE void set(TY v) {} + static FORCEINLINE TY get() { return readval; } + static FORCEINLINE void makeOutput(TY) {} + static FORCEINLINE void makeInput(TY) {} + static FORCEINLINE void toggle(TY) {} + static FORCEINLINE TY isOutput() { return 0; } + static FORCEINLINE TY isInput() { return 0; } + + template + static FORCEINLINE void setbit() {} + template + static FORCEINLINE void clearbit() {} +}; + +struct DummyPort : public DummyPortBase +{ +}; + +struct DummyPin : PinBase +{ +}; + +// Merges two distinct ports into a single virtual port that has the size of both ports combined +template +struct Port2x +{ + typedef bool is_port_tag; + typedef TY value_type; + typedef typename Port1::value_type hw_type; + typedef typename Port2::value_type hw_type2; + //static_assert(is_same::value, "Ports must have same underlying type"); + static_assert(sizeof(value_type) == sizeof(hw_type) + sizeof(hw_type2), "Combined type must be of same length than both hardware ports"); + + union Split + { + struct { hw_type lo; hw_type2 hi; } hw; + value_type both; + }; + static FORCEINLINE void set(TY v) { Split s; s.both = v; Port1::set(s.hw.lo); Port2::set(s.hw.hi); } + static FORCEINLINE TY get() { Split s; s.hw.lo = Port1::get(); s.hw.hi = Port2::get(); return s.both; } + static FORCEINLINE void makeOutput(TY ddr = TY(-1)) { Split s; s.both = ddr; Port1::makeOutput(s.hw.lo); Port2::makeOutput(s.hw.hi); } + static FORCEINLINE void toggle(TY v) { Split s; s.both = v; Port1::toggle(s.hw.lo); Port2::toggle(s.hw.hi); } + static FORCEINLINE TY readOutput() { Split s; s.hw.lo = Port1::readOutput(); s.hw.hi = Port2::readOutput(); return s.both; } +}; + +// Merges parts of 2 ports into a single virtual port that takes some bits from each port +// Warning: Port setting is dirty -- unused bits in each port are cleared to 0 +template +struct SplitPort_Dirty +{ + typedef bool is_port_tag; + typedef TY value_type; + typedef typename Port1::value_type hw_type; + typedef typename Port2::value_type hw_type2; + //static_assert(is_same::value, "Ports must have same underlying type"); + static_assert(sizeof(value_type) == sizeof(hw_type), "Combined type must be of same length as each hardware port"); + static_assert(sizeof(value_type) == sizeof(hw_type2), "Combined type must be of same length as each hardware port"); + + static FORCEINLINE void set(TY v) { Port1::set(v & Mask1); Port2::set(v & Mask2); } + static FORCEINLINE TY get() { return (Port1::get() & Mask1) | (Port2::get() & Mask2); } + static FORCEINLINE void makeOutput(TY ddr = TY(-1)) { Port1::makeOutput(ddr & Mask1); Port2::makeOutput(ddr & Mask2); } + static FORCEINLINE void toggle(TY v) { Port1::toggle(v & Mask1); Port2::toggle(v & Mask2); } + static FORCEINLINE TY readOutput() { return (Port1::readOutput() & Mask1) | (Port2::readOutput() & Mask2); } +}; + + + +} // end namespace fglcd + diff --git a/democode/src/demolib/fglcd/mcu_avr.h b/democode/src/demolib/fglcd/mcu_avr.h new file mode 100644 index 0000000..1d8014e --- /dev/null +++ b/democode/src/demolib/fglcd/mcu_avr.h @@ -0,0 +1,614 @@ +#pragma once + +// AVR specific include +// Warning: Inline asm ahead! The code the compiler would produce otherwise is hot garbage, +// fortunately with a bit of asm and poking it does the right thing and generates good enough code. + +#if defined(_AVR_IOM8_H_) || defined(_AVR_IOM16_H_) || defined(_AVR_IOM32_H_) +#define FGLCD_IS_OLD_AVR +#endif + +#ifndef FGLCD_IS_OLD_AVR +#define FGLCD_AVR_WRITE_PINX_TO_TOGGLE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include "mcu.h" +#include "macros.h" +#include "mcu_avr_intrin.h" +#include "util.h" + +#ifdef FGLCD_ASM +#define FGLCD_ASM_AVR +#endif + + +#ifdef RAMPZ +# ifndef FGLCD_PROGMEM_FAR +# define FGLCD_PROGMEM_FAR __attribute__((__section__(".fini0"))) +# endif +# define fglcd_get_farptr(x) pgm_get_far_address(x) +//# define PFSTR(s) (__extension__({static const char __c[] FGLCD_PROGMEM_FAR = (s); &__c[0];})) /* adapted from avr-libc */ +#else +# ifndef FGLCD_PROGMEM_FAR +# define FGLCD_PROGMEM_FAR PROGMEM +# endif +# define fglcd_get_farptr(x) (reinterpret_cast(x)) +//# define PFSTR(s) PSTR(s) +#endif + +#define StackAlloc(x) alloca(x) + +namespace fglcd { + +struct RAMPZBackup +{ +#ifdef RAMPZ + const uint8_t zhh; +#endif + FORCEINLINE RAMPZBackup() +#ifdef RAMPZ + : zhh(RAMPZ) +#endif + {} + + FORCEINLINE ~RAMPZBackup() + { +#ifdef RAMPZ + RAMPZ = zhh; +#endif + } +}; + +#ifdef RAMPZ +typedef uint_farptr_t FarPtr; +#else +typedef const unsigned char* FarPtr; +#endif + +typedef u8 unative; + +#if 1 +template +struct Port : public PortBase > +{ + typedef PortBase > Base; + typedef typename Base::IO IO; +#ifdef FGLCD_AVR_WRITE_PINX_TO_TOGGLE + static FORCEINLINE void toggle(TY v) { IO::_writeIn(v); } +#endif // else use default impl + + // Write to MMIO regs explicitly as a hint for the compiler to use sbi/cbi instructions instead of read-modify-write + template + static FORCEINLINE void setbit() { *IO::hwWrite() |= TY(1u << bit); } + template + static FORCEINLINE void clearbit() { *IO::hwWrite() &= ~TY(1u << bit); } +}; + +#else + +template +struct Port + { + typedef bool is_port_tag; + typedef TY value_type; + + static constexpr volatile TY * const hwWrite = (volatile TY*)aWP; + static constexpr volatile TY * const hwRead = (volatile TY*)aRP; + static constexpr volatile TY * const hwDDR = (volatile TY*)aDDR; + + static FORCEINLINE void set(TY v) { *hwWrite = v; } + static FORCEINLINE TY get() { return *hwRead; } + static FORCEINLINE TY readOutput() { return *hwWrite; } + static FORCEINLINE void makeOutput(TY ddr = ~TY(0)) { *hwDDR = ddr; } + static FORCEINLINE void makeInput(TY ddr = ~TY(0)) { makeOutput(~ddr); } + static FORCEINLINE TY isOutput() { return *hwDDR; } + static FORCEINLINE TY isInput() { return ~isOutput(); } + +#ifdef FGLCD_AVR_WRITE_PINX_TO_TOGGLE + static FORCEINLINE void toggle(TY v) { *hwRead = v; } +#endif // else use default impl + + // Write to MMIO regs explicitly as a hint for the compiler to use sbi/cbi instructions instead of read-modify-write + template + static FORCEINLINE void setbit() { *hwWrite |= value_type(1 << bit); } + template + static FORCEINLINE void clearbit() { *hwWrite &= ~value_type(1 << bit); } +}; + +#endif + +template +struct Pin : public PinBase +{ +}; + +FORCEINLINE void delay_ms(u32 ms) +{ + _delay_ms(ms); +} +FORCEINLINE void delay_us(u32 us) +{ + _delay_us(us); +} +FORCEINLINE void delay_cyc(u32 cyc) +{ + __builtin_avr_delay_cycles(cyc); +} + +struct NoInterrupt +{ + const u8 sreg; + FORCEINLINE NoInterrupt() : sreg(SREG) { cli(); } + FORCEINLINE ~NoInterrupt() { SREG = sreg; } +}; + +FORCEINLINE static uint8_t uhh8(uint32_t w) +{ + union layout + { + uint32_t i; + struct { uint8_t lo, hi, hhi, hhhi; } b; + } u; + u.i = w; + return u.b.hhi; +} + + + +namespace memtype { + +struct RAM +{ + typedef const unsigned char * Pointer; + typedef const void * VoidPointer; + typedef RAIINop SegmentBackup; + + template static FORCEINLINE T read(const void *p, ptrdiff_t offs = 0) { return static_cast(p)[offs]; } + + template + static FORCEINLINE void Memset_inl(void * dst, const uint8_t c, L len) + { +#ifdef FGLCD_ASM_AVR + FGLCD_DUFF8(L, len, { + asm volatile( + "st %a[dst]+,%[c] \n\t" + : [dst] "+x" ((volatile void*)dst) /*outputs*/ + : [c] "r" (c) /* inputs */ + ); + }); +#else + memset(dst, c, len); +#endif + } + + static NOINLINE void Memset(void * dst, const uint8_t c, unsigned len) + { +#ifdef FGLCD_ASM_AVR + FGLCD_DUFF32(unsigned, len, { + asm volatile( + "st %a[dst]+,%[c] \n\t" + : [dst] "+x" ((volatile void*)dst) /*outputs*/ + : [c] "r" (c) /* inputs */ + ); + }); +#else + memset(dst, c, len); +#endif + } + + template + static FORCEINLINE void ForRange_inl(VoidPointer p, L sz, Action& a) + { +#ifdef FGLCD_ASM_AVR + uint8_t b; + FGLCD_DUFF4(L, sz, { + asm volatile( + "ld %[b],%a[p]+ \n\t" + : [b] "=&r" (b), [p] "+x" (p) /*outputs*/ + : /* inputs */ + ); + a(b); + }); +#else + Pointer pp = (Pointer)p; + while(sz--) + a(*pp++); +#endif + } + + template + static NOINLINE void ForRange(VoidPointer begin, unsigned sz, Action& a) + { + ForRange_inl(begin, sz, a); + } + + static NOINLINE void Memcpy(void *dst, VoidPointer src, unsigned len) + { +#ifdef FGLCD_ASM_AVR + FGLCD_DUFF32(unsigned, len, { + asm volatile( + "ld r0, %a[src]+ \n\t" + "st %a[dst]+, r0 \n\t" + : [dst] "+x" ((volatile void*)dst), [src] "+z" ((volatile void*)src) /*outputs*/ + : /* inputs */ + : "r0" /*clobber*/ + ); + }); +#else + memcpy(dst, src, len); +#endif + } + + template + static FORCEINLINE void Memcpy_inl(void *dst, VoidPointer src, L len) + { +#ifdef FGLCD_ASM_AVR + FGLCD_DUFF4(L, len, { + asm volatile( + "ld r0, %a[src]+ \n\t" + "st %a[dst]+, r0 \n\t" + : [dst] "+x" ((volatile void*)dst), [src] "+z" ((volatile void*)src) /*outputs*/ + : /* inputs */ + : "r0" /*clobber*/ + ); + }); +#else + memcpy(dst, src, len); +#endif + } + + static FORCEINLINE uint8_t readIncSafe(VoidPointer& p) { return readIncFast(p); } + static FORCEINLINE uint8_t readIncSafe(Pointer& p) { return readIncFast(p); } + + static FORCEINLINE uint8_t readIncFast(Pointer& p) { return intrin::_avr_ld_postinc((const void**)&p); } + static FORCEINLINE uint8_t readIncFast(VoidPointer& p) { return readIncFast((Pointer&)p); } + + static FORCEINLINE void storeIncFast(uint8_t *& p, uint8_t v) { return intrin::_avr_st_postinc((void**)&p, v); } + static FORCEINLINE void storeIncFast(void*& p, uint8_t v) { storeIncFast((uint8_t*&)p, v); } + + static FORCEINLINE void SetSegmentPtr(Pointer p) {} + static FORCEINLINE void fixFarPtr(VoidPointer& p) {} + static FORCEINLINE void fixFarPtr(Pointer& p) {} +}; + +struct Progmem +{ + typedef const u8 * Pointer; + typedef const void * VoidPointer; + typedef RAIINop SegmentBackup; + + template static FORCEINLINE T read(const void *p, ptrdiff_t offs = 0) + { + // Having this in a union also prevents calling any ctor + union + { + T val; + uint8_t buf[sizeof(T)]; + } u; + const T *src = ((const T*)p) + offs; + Memcpy_inl::type>(u.buf, src, sizeof(T)); + return u.val; + } + + template + static FORCEINLINE void ForRange_inl(VoidPointer p, unsigned sz, Action& a) + { +#ifdef FGLCD_ASM_AVR + uint8_t b; + FGLCD_DUFF4(unsigned, sz, { + asm volatile( + "lpm %[b],%a[p]+ \n\t" + : [b] "=&r" (b), [p] "+z" (p) /*outputs*/ + : /* inputs */ + ); + a(b); + }); +#else + Pointer pp = (Pointer)pp; + while(sz--) + { + a(pgm_read_byte(pp)); + ++pp; + } +#endif + } + + template + static NOINLINE void ForRange(VoidPointer pf, unsigned sz, Action& a) + { + ForRange_inl(pf, sz, a); + } + + template + static FORCEINLINE void Memcpy_inl(void *dst, VoidPointer src, L sz) + { +#ifdef FGLCD_ASM_AVR + FGLCD_DUFF4(L, sz, { + asm volatile( + "lpm r0,%a[src]+ \n\t" + "st %a[dst]+,r0 \n\t" + : [src] "+z" (src), [dst] "+x" ((volatile void*)dst) /*outputs*/ + : /* inputs */ + : "r0" /* clobber */ + ); + }); +#else + memcpy_P(dst, src, sz); +#endif + } + static NOINLINE void Memcpy(void *dst, VoidPointer src, unsigned sz) + { +#ifdef FGLCD_ASM_AVR + FGLCD_DUFF32(unsigned, sz, { + asm volatile( + "lpm r0,%a[src]+ \n\t" + "st %a[dst]+,r0 \n\t" + : [src] "+z" (src), [dst] "+x" ((volatile void*)dst) /*outputs*/ + : /* inputs */ + : "r0" /* clobber */ + ); + }); +#else + memcpy_P(dst, src, sz); +#endif + } + + static FORCEINLINE uint8_t readIncSafe(VoidPointer& p) { return readIncFast(p); } + static FORCEINLINE uint8_t readIncSafe(Pointer& p) { return readIncFast(p); } + + static FORCEINLINE uint8_t readIncFast(VoidPointer& p) { return intrin::_avr_lpm_postinc(&p); } + static FORCEINLINE uint8_t readIncFast(Pointer& p) { return readIncFast((VoidPointer&)p); } + + static FORCEINLINE void SetSegmentPtr(FarPtr) {} + static FORCEINLINE void fixFarPtr(Pointer& p) {} + static FORCEINLINE void fixFarPtr(VoidPointer& p) {} +}; +#ifdef RAMPZ +struct ProgmemFar +{ + typedef FarPtr Pointer; + typedef FarPtr VoidPointer; + typedef RAMPZBackup SegmentBackup; + + template static T read(FarPtr p, ptrdiff_t offs = 0) + { + // Having this in a union also prevents calling any ctor + union + { + T val; + uint8_t buf[sizeof(T)]; + } u; + FarPtr src = p + (offs * sizeof(T)); + Memcpy_inl::type>(u.buf, src, sizeof(T)); + return u.val; + } + + template + static FORCEINLINE void ForRange_inl(VoidPointer pf, L sz, Action& a) + { + SegmentBackup seg; + RAMPZ = uhh8(pf); + uint8_t b; + void *p = (void*)pf; + FGLCD_DUFF4(L, sz, { + asm volatile( + "elpm %[b],%a[p]+ \n\t" + : [b] "=&r" (b), [p] "+z" ((volatile void*)p) /*outputs*/ + : /* inputs */ + ); + a(b); + }); + } + + template + static NOINLINE void ForRange(VoidPointer pf, unsigned sz, Action& a) + { + ForRange_inl(pf, sz, a); + } + + template + static FORCEINLINE void Memcpy_inl(void *dst, VoidPointer srcf, L sz) + { + SegmentBackup seg; + RAMPZ = uhh8(srcf); + void *src = (void*)srcf; + FGLCD_DUFF4(L, sz, { + asm volatile( + "elpm r0,%a[src]+ \n\t" + "st %a[dst]+,r0 \n\t" + : [src] "+z" (src), [dst] "+x" ((volatile void*)dst) /*outputs*/ + : /* inputs */ + : "r0" /* clobber */ + ); + }); + } + static NOINLINE void Memcpy(void *dst, VoidPointer srcf, unsigned sz) + { + SegmentBackup seg; + RAMPZ = uhh8(srcf); + void *src = (void*)srcf; + FGLCD_DUFF32(unsigned, sz, { + asm volatile( + "elpm r0,%a[src]+ \n\t" + "st %a[dst]+,r0 \n\t" + : [src] "+z" (src), [dst] "+x" ((volatile void*)dst) /*outputs*/ + : /* inputs */ + : "r0" /* clobber */ + ); + }); + } + + static FORCEINLINE uint8_t readIncSafe(VoidPointer& p) + { + SetSegmentPtr(p); + const uint8_t c = readIncFast(p); + fixFarPtr(p); + return c; + } + + static FORCEINLINE void SetSegmentPtr(VoidPointer p) + { + RAMPZ = uhh8(p); + } + + // set RAMPZ before calling this! + static FORCEINLINE uint8_t readIncFast(VoidPointer& p) + { + return intrin::_avr_elpm_postinc((const void**)&p); + } + + static FORCEINLINE void fixFarPtr(VoidPointer& p) + { + union + { + Pointer pp; + const void *lo; + uint8_t c[sizeof(Pointer)]; + }; + pp = (Pointer)p; + c[3] = RAMPZ; + } +}; +#else + typedef Progmem ProgmemFar; +#endif +} // end namespace memtype + + +// ok, this used to work in an older avr-gcc but starting from 6.0 it's barfing all over the place +// so use that namespace trick to make it obey +namespace _avrportdef { +#define _EXPAND(x) x // freaky indirection to make sure the ## expansion happens before macro expansion +#define AVRPORT(_, pin, port, ddr) \ + static const uintptr_t _EXPAND(pin##_ADDR) = _SFR_MEM_ADDR(pin); \ + static const uintptr_t _EXPAND(port##_ADDR) = _SFR_MEM_ADDR(port); \ + static const uintptr_t _EXPAND(ddr##_ADDR) = _SFR_MEM_ADDR(ddr); +#include "mcu_avr_portdef.h" +#undef AVRPORT +} +#define AVRPORT(name, pin, port, ddr) \ + typedef fglcd::Port name; +#include "mcu_avr_portdef.h" +#undef AVRPORT +#undef _EXPAND + +struct InsideInterrupt {}; + + +FORCEINLINE static uint8_t ulo8(uint16_t w) +{ + return uint8_t(w); +} + +FORCEINLINE static uint8_t uhi8(uint16_t w) +{ + union layout + { + uint16_t i; + struct { uint8_t lo, hi; } b; + } u; + u.i = w; + return u.b.hi; +} + +FORCEINLINE static uint16_t make16(uint8_t lo, uint8_t hi) +{ + union layout + { + uint16_t i; + struct { uint8_t lo, hi; } b; + } u; + u.b.lo = lo; + u.b.hi = hi; + return u.i; +} + +#if 0 + +namespace SPI { + enum DataOrder + { + MSB_FIRST = 0, + LSB_FIRST = 1, + }; + inline void enable(DataOrder ord) + { + SPCR |= _BV(SPE) | (ord << DORD); + SPSR |= _BV(SPI2X); + } + FORCEINLINE void wait() + { + while(!(SPSR & (1< +struct Connection_XMEM_8 : public _Connection_XMEM_8 +{ +}; + +template +struct Connection_XMEM_16: public _Connection_XMEM_16 +{ +}; +#endif + + +} // end namespace fglcd diff --git a/democode/src/demolib/fglcd/mcu_avr_intrin.h b/democode/src/demolib/fglcd/mcu_avr_intrin.h new file mode 100644 index 0000000..1322204 --- /dev/null +++ b/democode/src/demolib/fglcd/mcu_avr_intrin.h @@ -0,0 +1,123 @@ +#pragma once + +namespace intrin { + +static FORCEINLINE uint8_t _avr_lpm_postinc(const void ** p) +{ + uint8_t ret; + asm volatile( + "lpm %[ret],%a[p]+ \n\t" + : [ret] "=&r" (ret), [p] "+z" (*p) /*outputs*/ + ); + return ret; +} + +static FORCEINLINE uint8_t _avr_elpm_postinc(const void ** p) +{ + uint8_t ret; + asm volatile( + "elpm %[ret],%a[p]+ \n\t" + : [ret] "=&r" (ret), [p] "+z" (*p) /*outputs*/ + ); + return ret; +} + +static FORCEINLINE uint8_t _avr_ldx_postinc(const void ** p) +{ + uint8_t ret; + asm volatile( + "ld %[ret],%a[p]+ \n\t" + : [ret] "=&r" (ret), [p] "+x" (*p) /*outputs*/ + ); + return ret; +} + +static FORCEINLINE uint8_t _avr_ldy_postinc(const void ** p) +{ + uint8_t ret; + asm volatile( + "ld %[ret],%a[p]+ \n\t" + : [ret] "=&r" (ret), [p] "+y" (*p) /*outputs*/ + ); + return ret; +} + +static FORCEINLINE uint8_t _avr_ld_postinc(const void ** p) +{ + uint8_t ret; + asm volatile( + "ld %[ret],%a[p]+ \n\t" + : [ret] "=&r" (ret), [p] "+e" (*p) /*outputs*/ + ); + return ret; +} + +static FORCEINLINE void _avr_stx_postinc(void ** p, uint8_t v) +{ + asm volatile( + "st %a[p]+,%[v] \n\t" + : [p] "+x" (*p) /*outputs*/ + : [v] "r" (v) + ); +} + +static FORCEINLINE void _avr_sty_postinc(void ** p, uint8_t v) +{ + asm volatile( + "st %a[p]+,%[v] \n\t" + : [p] "+y" (*p) /*outputs*/ + : [v] "r" (v) + ); +} + +static FORCEINLINE void _avr_st_postinc(void ** p, uint8_t v) +{ + asm volatile( + "st %a[p]+,%[v] \n\t" + : [p] "+e" (*p) /*outputs*/ + : [v] "r" (v) + ); +} + +//--------------------------------------------------------------------------- + +#if 0 + +// BOTH UNTESTED + +// count leading ones and modify input +static FORCEINLINE uint8_t _avr_clo8_modify(uint8_t& v) +{ + uint8_t ret = 0; + asm volatile( + "loop_%=: \n\t" + "bst %[v], 7 \n\t" /* remember bit we're going to shift into carry */ + "lsl %[v] \n\t" /* shift highest bit into carry */ + "adc %[ret], 0 \n\t" /* accumulate */ + "brts loop_%= \n\t" /* if bit was set, keep going*/ + "lsr %[v] \n\t" /* we shifted one too many, undo that */ + : [ret] "=&r" (ret), [v] "+r" (v) /*outputs*/ + ); + return ret; +} + + +static FORCEINLINE uint8_t _avr_clz8_modify(uint8_t& v) +{ + uint8_t ret = 0; + asm volatile( + "loop_%=: \n\t" + "bst %[v], 7 \n\t" + "lsl %[v] \n\t" + "subi %[ret],lo8(-(1)) \n\t" // ret must be upper register + "brtc loop_%= \n\t" + "lsr %[v] \n\t" + : [ret] "=&d" (ret), [v] "+r" (v) /*outputs*/ + ); + return ret; +} +#endif + + + +} // end namespace intrin diff --git a/democode/src/demolib/fglcd/mcu_avr_portdef.h b/democode/src/demolib/fglcd/mcu_avr_portdef.h new file mode 100644 index 0000000..f2d9130 --- /dev/null +++ b/democode/src/demolib/fglcd/mcu_avr_portdef.h @@ -0,0 +1,36 @@ +#ifdef PORTA +AVRPORT(PortA, PINA, PORTA, DDRA) +#endif +#ifdef PORTB +AVRPORT(PortB, PINB, PORTB, DDRB) +#endif +#ifdef PORTC +AVRPORT(PortC, PINC, PORTC, DDRC) +#endif +#ifdef PORTD +AVRPORT(PortD, PIND, PORTD, DDRD) +#endif +#ifdef PORTE +AVRPORT(PortE, PINE, PORTE, DDRE) +#endif +#ifdef PORTF +AVRPORT(PortF, PINF, PORTF, DDRF) +#endif +#ifdef PORTG +AVRPORT(PortG, PING, PORTG, DDRG) +#endif +#ifdef PORTH +AVRPORT(PortH, PINH, PORTH, DDRH) +#endif +#ifdef PORTI +AVRPORT(PortI, PINI, PORTI, DDRI) +#endif +#ifdef PORTJ +AVRPORT(PortJ, PINJ, PORTJ, DDRJ) +#endif +#ifdef PORTK +AVRPORT(PortK, PINK, PORTK, DDRK) +#endif +#ifdef PORTL +AVRPORT(PortL, PINL, PORTL, DDRL) +#endif diff --git a/democode/src/demolib/fglcd/mcu_pc.h b/democode/src/demolib/fglcd/mcu_pc.h new file mode 100644 index 0000000..318015a --- /dev/null +++ b/democode/src/demolib/fglcd/mcu_pc.h @@ -0,0 +1,191 @@ +#pragma once + +#ifndef NO_SDL +#include +#endif + +#include +#include +#include +#include "util.h" + +#pragma warning(disable: 4530) +#pragma warning(disable: 4577) + +#define MCU_IS_PC + +#define PROGMEM +#define FGLCD_PROGMEM_FAR + +#define PSTR(s) (s) +#define PFSTR(s) (s) + +#define snprintf_P snprintf +#define strncpy_P strncpy + +#define fglcd_get_farptr(x) (reinterpret_cast(&x[0])) + +#define StackAlloc(x) alloca(x) // TODO: some fancy thing? + +#if defined(unix) && (defined(__i386__) || defined(__x86_64__)) +static inline void debug_break(void) { + __asm__ volatile("int $0x03"); +} + +#elif defined(_WIN32) +# define debug_break __debugbreak + +#else +# error "No breakpoint implementation defined" +#endif + +#ifndef NO_SDL +extern SDL_mutex *g_interruptMutex; +#endif + + +namespace fglcd { + +typedef const unsigned char* FarPtr; +typedef unsigned unative; + +template +struct Port : public PortBase > +{ +}; + +template +struct Pin : public PinBase +{ +}; + + +namespace memtype { + +struct RAM +{ + typedef const unsigned char * Pointer; + typedef const void * VoidPointer; + typedef RAIINop SegmentBackup; + template static FORCEINLINE T read(const void *p, ptrdiff_t offs = 0) { return static_cast(p)[offs]; } + + template + static FORCEINLINE void Memcpy_inl(void *dst, VoidPointer src, L len) { memcpy(dst, src, len); } + static FORCEINLINE void Memcpy(void *dst, VoidPointer src, size_t len) { memcpy(dst, src, len); } + + template + static FORCEINLINE void Memset_inl(void *dst, const uint8_t c, L len) { memset(dst, c, len); } + static FORCEINLINE void Memset(void *dst, const uint8_t c, size_t len) { memset(dst, c, len); } + + template + static FORCEINLINE void ForRange_inl(VoidPointer begin, size_t sz, Action& a) + { + Pointer p = (Pointer)begin; + Pointer const end = p + sz; + while(p < end) + a(*p++); + } + + template + static NOINLINE void ForRange(VoidPointer begin, size_t sz, Action& a) + { + ForRange_inl(begin, sz, a); + } + + static FORCEINLINE uint8_t readIncSafe(VoidPointer& p) { return readIncFast(p); } + static FORCEINLINE uint8_t readIncSafe(Pointer& p) { return readIncFast(p); } + + static FORCEINLINE void SetSegmentPtr(FarPtr p) {} + static FORCEINLINE uint8_t readIncFast(Pointer& p) { return *p++; } + static FORCEINLINE uint8_t readIncFast(VoidPointer& p) { return readIncFast((Pointer&)p); } + static FORCEINLINE void fixFarPtr(VoidPointer& p) {} + static FORCEINLINE void fixFarPtr(Pointer& p) {} + static FORCEINLINE void storeIncFast(uint8_t*& p, uint8_t v) { *p++ = v; } + static FORCEINLINE void storeIncFast(void*& p, uint8_t v) { storeIncFast((uint8_t*&)p, v); } +}; + +typedef RAM Progmem; +typedef RAM ProgmemFar; +} + +struct SegmentBackup +{ + FORCEINLINE SegmentBackup() {} + FORCEINLINE ~SegmentBackup() {} +}; + +FORCEINLINE static uint16_t make16(uint8_t lo, uint8_t hi) +{ + return (hi << 8u) | lo; +} + +FORCEINLINE static uint8_t ulo8(uint16_t w) +{ + return uint8_t(w); +} + +FORCEINLINE static uint8_t uhi8(uint16_t w) +{ + return uint8_t(w >> 8); +} + +FORCEINLINE static uint8_t uhh8(uint32_t w) +{ + return uint8_t(w >> 16); +} + + + +FORCEINLINE void delay_ms(u32 ms) +{ +#ifndef NO_SDL + SDL_Delay(ms); +#endif +} +FORCEINLINE void delay_us(u32 us) +{ + // too short anyway +} +FORCEINLINE void delay_cyc(u32 cyc) +{ + // lolnope +} + +struct _SDLGlobalLock +{ + FORCEINLINE _SDLGlobalLock() + { +#ifndef NO_SDL + SDL_LockMutex(g_interruptMutex); +#endif + } + FORCEINLINE ~_SDLGlobalLock() + { +#ifndef NO_SDL + SDL_UnlockMutex(g_interruptMutex); +#endif + } +}; + +typedef _SDLGlobalLock NoInterrupt; +typedef _SDLGlobalLock InsideInterrupt; + + +typedef fglcd::Port PortA; +typedef fglcd::Port PortB; +typedef fglcd::Port PortC; +typedef fglcd::Port PortD; +typedef fglcd::Port PortE; +typedef fglcd::Port PortF; +typedef fglcd::Port PortG; +typedef fglcd::Port PortH; +typedef fglcd::Port PortI; +typedef fglcd::Port PortJ; +typedef fglcd::Port PortK; +typedef fglcd::Port PortL; + + +} // end namespace fglcd + + + diff --git a/democode/src/demolib/fglcd/overrides.h b/democode/src/demolib/fglcd/overrides.h new file mode 100644 index 0000000..781a267 --- /dev/null +++ b/democode/src/demolib/fglcd/overrides.h @@ -0,0 +1,26 @@ +// Variants of libc functions that are much faster than those in avr-libc +// Not standards compliant (returns void instead of void*), +// so enable them only on when not on the PC, as they may conflict with system headers. + +#pragma once + +#include "mcu.h" + +#ifndef MCU_IS_PC + +#ifdef memset +#undef memset +#endif +#define memset(dst, b, n) fglcd::RAM::Memset((dst), (b), (n)) + +#ifdef memcpy +#undef memcpy +#endif +#define memcpy(dst, src, n) fglcd::RAM::Memcpy((void*)(dst), (const void*)(src), (n)) + +#ifdef memcpy_P +#undef memcpy_P +#endif +#define memcpy_P(dst, src, n) fglcd::Progmem::Memcpy((void*)(dst), (const void*)(src), (n)) + +#endif diff --git a/democode/src/demolib/fglcd/presets.h b/democode/src/demolib/fglcd/presets.h new file mode 100644 index 0000000..c701795 --- /dev/null +++ b/democode/src/demolib/fglcd/presets.h @@ -0,0 +1,108 @@ +#pragma once + +#include "chip_ili9341.h" +#include "chip_ili9481.h" +#include "chip_ili9486.h" +#include "chip_hx8357.h" + +namespace fglcd { +namespace preset { + +struct Dummy_LCD_config +{ + typedef DummyPin WrxPin; + typedef DummyPin DCxPin; + typedef DummyPin CSPin; + typedef DummyPin ResetPin; + typedef DummyPort CmdPort; + typedef DummyPort DataPort; + typedef Con_Port CmdCon; + typedef Con_Port DataCon; + typedef Interface Iface; + typedef LcdBase lcdtype; +}; +typedef typename Dummy_LCD_config::lcdtype Dummy_LCD; + + +#if defined(PORTC) || defined(MCU_IS_PC) // FIXME: HACK +template class Chip> +struct Uno_8Bit_Shield_templ +{ + //typedef Pin RdPin; + typedef Pin WrxPin; + typedef Pin DCxPin; + typedef Pin CSPin; + typedef Pin ResetPin; + // On an Uno, The first 2 bits come from PORTC, the rest is PORTD + typedef SplitPort_Dirty Port8Bit; + // But the data port still uses 16 bit colors, so need to use the 8bit port twice + typedef Port2x Port16Bit; + typedef Con_Port CmdCon; + typedef Con_Port DataCon; + typedef Interface Iface; + typedef LcdBase lcdtype; +}; + +typedef typename Uno_8Bit_Shield_templ::lcdtype ILI9341_Uno_Shield; +typedef typename Uno_8Bit_Shield_templ::lcdtype ILI9486_Uno_Shield; +#endif + +#if defined(PORTG) || defined(MCU_IS_PC) // FIXME: HACK +template class Chip> +struct Mega_16Bit_Shield_templ +{ + typedef Pin WrxPin; + typedef Pin DCxPin; + typedef Pin CSPin; + typedef Pin ResetPin; + typedef PortC CmdPort; + typedef Port2x DataPort; + typedef Con_Port CmdCon; + typedef Con_Port DataCon; + typedef Interface Iface; + typedef LcdBase lcdtype; +}; + +typedef typename Mega_16Bit_Shield_templ::lcdtype ILI9481_Mega_Shield; +typedef typename Mega_16Bit_Shield_templ::lcdtype ILI9486_Mega_Shield; +typedef typename Mega_16Bit_Shield_templ::lcdtype HX8357C_Mega_Shield; +#endif +/* +struct ST7783_SPI_LCD_Shield_2_4inch_UNO_config +{ + typedef Pin WrxPin; + typedef Pin DCxPin; + typedef Pin CSPin; + typedef Pin ResetPin; + typedef SpiPort CmdPort; + typedef SpiPort DataPort; + struct SPIInit + { + FORCEINLINE void init() + { + SPI::enable(SPI::MSB_FIRST); + } + }; + typedef Interface Iface; + typedef LcdBase lcdtype; + +}; +typedef typename ST7783_SPI_LCD_Shield_2_4inch_UNO_config::lcdtype T7783_SPI_LCD_Shield_2_4inch_UNO; +*/ + +/* +struct ILI9481_Mega_Shield_XMEM_config +{ + typedef DummyPin WrxPin; + typedef Pin DCxPin; + typedef Pin CSPin; + typedef Pin ResetPin; + typedef Port2x DataPort; + typedef Interface Iface; + typedef LcdBase lcdtype; +}; +typedef typename ILI9481_Mega_Shield_XMEM_config::lcdtype ILI9481_Mega_Shield_XMEM; +*/ + +} // end namespace preset +} // end namespace fglcd diff --git a/democode/src/demolib/fglcd/trigger.h b/democode/src/demolib/fglcd/trigger.h new file mode 100644 index 0000000..18bda4d --- /dev/null +++ b/democode/src/demolib/fglcd/trigger.h @@ -0,0 +1,84 @@ +#pragma once + +#include "mcu.h" + +namespace fglcd { + +template +struct _TriggerBase +{ + typedef PIN Pin; + //static_assert(has_pin_tag::value, "Pin is not a pin"); + typedef bool is_trigger_tag; +}; + +struct DummyTrigger : public _TriggerBase +{ + static FORCEINLINE void trigger() {} + static FORCEINLINE void set() {} + static FORCEINLINE void clear() {} +}; + +template +struct ToggleTrigger : public _TriggerBase +{ + static FORCEINLINE void trigger() { PIN::pulse(); } + static FORCEINLINE void set() { PIN::toggle(); } + static FORCEINLINE void clear() { PIN::toggle(); } +}; + +template +struct LowTrigger : public _TriggerBase +{ + static FORCEINLINE void trigger() { PIN::pulse_lo(); } + static FORCEINLINE void set() { PIN::lo(); } + static FORCEINLINE void clear() { PIN::hi(); } +}; + +template +struct HighTrigger : public _TriggerBase +{ + static FORCEINLINE void trigger() { PIN::pulse_hi(); } + static FORCEINLINE void set() { PIN::hi(); } + static FORCEINLINE void clear() { PIN::lo(); } +}; + +// ------------------ +// Derived triggers -- take existing trigger, add hold times + +template +struct TriggerPulse : public Trig +{ + //static_assert(has_trigger_tag::value, "Trig is not a trigger"); +}; + +template +struct TriggerHoldMS : public Trig +{ + //static_assert(has_trigger_tag::value, "Trig is not a trigger"); + static FORCEINLINE void trigger() { set(); clear(); } + static FORCEINLINE void set() { Trig::set(); delay_ms(ms); } + static FORCEINLINE void clear() { Trig::clear(); } +}; + +template +struct TriggerHoldCycles : public Trig +{ + //static_assert(has_trigger_tag::value, "Trig is not a trigger"); + static FORCEINLINE void trigger() { set(); clear(); } + static FORCEINLINE void set() { Trig::set(); delay_cyc(cyc); } + static FORCEINLINE void clear() { Trig::clear(); } +}; + +template +struct TriggerWaitMS : public Trig +{ + //static_assert(has_trigger_tag::value, "Trig is not a trigger"); + static FORCEINLINE void trigger() { set(); clear(); } + static FORCEINLINE void set() { Trig::set(); } + static FORCEINLINE void clear() { Trig::clear(); delay_ms(ms); } +}; + + + +} // end namespace fglcd diff --git a/democode/src/demolib/fglcd/util.h b/democode/src/demolib/fglcd/util.h new file mode 100644 index 0000000..5ee9b89 --- /dev/null +++ b/democode/src/demolib/fglcd/util.h @@ -0,0 +1,140 @@ +#pragma once + +#include "def.h" +#include + +#ifdef FGLCD_DEBUG +void _assert_fail(const char *cond_P, const char *msg_P, unsigned line, int val, int val2); // must be defined by user somewhere +#endif + +namespace fglcd { + +template +struct TypeForSize +{ + typedef typename TypeSwitch< + N <= UINTMAX_C(0xff), + u8, + typename TypeSwitch< + N <= UINTMAX_C(0xffff), + u16, + typename TypeSwitch< + N <= UINTMAX_C(0xffffffff), + u32, + u64 + >::type + >::type + >::type type; +}; + +template +struct SizeConfig +{ + typedef typename TypeForSize::type type; + static const type total = type(A * B); + static_assert(total == A*B, "value truncated"); + static_assert(total / A == B, "wrong eval"); +}; + +template +struct has_port_tag +{ + typedef char yes[1]; + typedef char no[2]; + + template static yes& test(typename C::is_port_tag*); + template static no& test(...); + + enum { value = sizeof(test(0)) == sizeof(yes) }; +}; + +template +struct has_command_tag +{ + typedef char yes[1]; + typedef char no[2]; + + template static yes& test(typename C::is_command_tag*); + template static no& test(...); + + enum { value = sizeof(test(0)) == sizeof(yes) }; +}; + +template +struct has_trigger_tag +{ + typedef char yes[1]; + typedef char no[2]; + + template static yes& test(typename C::is_trigger_tag*); + template static no& test(...); + + enum { value = sizeof(test(0)) == sizeof(yes) }; +}; + +template +struct has_pin_tag +{ + typedef char yes[1]; + typedef char no[2]; + + template static yes& test(typename C::is_pin_tag*); + template static no& test(...); + + enum { value = sizeof(test(0)) == sizeof(yes) }; +}; + +template +struct has_con_tag +{ + typedef char yes[1]; + typedef char no[2]; + + template static yes& test(typename C::is_con_tag*); + template static no& test(...); + + enum { value = sizeof(test(0)) == sizeof(yes) }; +}; + +template +struct has_connection_tag +{ + typedef char yes[1]; + typedef char no[2]; + + template static yes& test(typename C::is_connection_tag*); + template static no& test(...); + + enum { value = sizeof(test(0)) == sizeof(yes) }; +}; + +// https://stackoverflow.com/questions/5100015/c-metafunction-to-determine-whether-a-type-is-callable +/*template +struct is_callable +{ +private: + typedef char(&yes)[1]; + typedef char(&no)[2]; + + struct Fallback { void operator()(); }; + struct Derived : T, Fallback { }; + + template struct Check; + + template + static yes test(...); + + template + static no test(Check*); + +public: + static const bool value = sizeof(test(0)) == sizeof(yes); +};*/ + +struct RAIINop +{ + FORCEINLINE RAIINop() {} + FORCEINLINE ~RAIINop() {} +}; + +} // end namespace fglcd diff --git a/democode/src/demolib/fontdef.h b/democode/src/demolib/fontdef.h new file mode 100644 index 0000000..d8223cb --- /dev/null +++ b/democode/src/demolib/fontdef.h @@ -0,0 +1,10 @@ +#pragma once + +enum FontDataType +{ + FONTTYPE_1BPP_4BIT_NPIX, + FONTTYPE_1BPP_LZ4COUNTS_NPIX, + FONTTYPE_IMAGE_LIST, + FONTTYPE_CBM64, +}; + diff --git a/democode/src/demolib/globals.cpp b/democode/src/demolib/globals.cpp new file mode 100644 index 0000000..5b12e2e --- /dev/null +++ b/democode/src/demolib/globals.cpp @@ -0,0 +1,15 @@ +#include "globals.h" + +#if defined(__AVR__) && (CFG_SCRATCH_ALIGNMENT == 256) +// The linker will place this at the lower end of RAM, which is aligned (linker map: 0x00800200) +// If it doesn't, something is either very fucked up or some idiot introduced a global somewhere. +#define SCRATCH_ALN +#else +# if CFG_SCRATCH_ALIGNMENT+0 +# define SCRATCH_ALN ALIGN(CFG_SCRATCH_ALIGNMENT) +# else +# define SCRATCH_ALN +# endif +#endif + +SCRATCH_ALN AllGlobals G; diff --git a/democode/src/demolib/globals.h b/democode/src/demolib/globals.h new file mode 100644 index 0000000..eecc651 --- /dev/null +++ b/democode/src/demolib/globals.h @@ -0,0 +1,84 @@ +#pragma once + +#include "cfg-demo.h" +#include "eventsystem.h" +#include "pmf_player_def.h" +#include "musicsync.h" + +#ifdef FGLCD_DEBUG +#include "debugthing.h" +#include "demodebug.h" +#endif + +#ifdef CFG_MUSIC_SYNC_CHANNELS +enum +{ + MaxMusicSyncChannels = ((0+CFG_MUSIC_SYNC_CHANNELS) < pmfplayer_max_channels) + ? (0+CFG_MUSIC_SYNC_CHANNELS) + : pmfplayer_max_channels +}; +#endif + +struct DemoGlobals +{ + volatile uint8_t partdone; +}; + +#ifdef FGLCD_DEBUG +struct DemoDebugGlobals +{ + // used by demolib + DebugThing device; + DebugVars vars; + + uint8_t numDebugInstances; + + // add some more? +}; +#endif + +#ifdef CFG_ENABLE_AUDIO +typedef pmf_audio_buffer AudioBufType; +struct DemoMusic +{ + AudioBufType audiobuf; + pmf_player player; +#ifdef CFG_MUSIC_SYNC_CHANNELS + musync::ChannelCallback sync[MaxMusicSyncChannels]; +#endif +}; +#endif + +struct AllGlobals +{ + // IMPORTANT: + // scratchblock *must* be aligned to a 256 byte boundary! + // One way would be to align it right here, which is problematic because it forces the entire struct to be aligned, + // which means its sizeof() will change. + // Since this is a struct only to group the variables nicely and there's only once instance, + // we align the struct singleton (see globals.cpp). + // This way sizeof() is really just the size of the struct without alignment, effectively allowing to use a bit more RAM. +#if CFG_SCRATCH_MAX_AREAS+0 + uint8_t scratchblock[CFG_SCRATCH_MAX_AREAS * CFG_SCRATCH_BLOCK_SIZE]; +#endif + + // mus.audiobuf.buffer *must* be aligned to a 256 byte boundary as well! +#ifdef CFG_ENABLE_AUDIO + DemoMusic mus; +#endif + +#if CFG_MAX_EVENTS+0 + evs::priv_Globals ev; +#endif + + DemoGlobals demo; + +#ifdef FGLCD_DEBUG + DemoDebugGlobals debug; +#endif +}; + + +extern AllGlobals G; + +#define MAXRAMFREE (CFG_MAXRAM - sizeof(AllGlobals) - CFG_RAM_SAFETY_AREA) diff --git a/democode/src/demolib/interpolator.cpp b/democode/src/demolib/interpolator.cpp new file mode 100644 index 0000000..2062a33 --- /dev/null +++ b/democode/src/demolib/interpolator.cpp @@ -0,0 +1,121 @@ +#include "interpolator.h" +#include "eventsystem.h" +#include "fglcd/macros.h" + +namespace interp { + +void InterpolatorBase::TickCallback(void *p) +{ + InterpolatorBase *base = (InterpolatorBase*)p; + if(base->_taskstate == 2) + { + base->_taskstate = 0; + return; + } + base->tick(); + base->registerTask(); +} + + +void InterpolatorBase::registerTask() +{ + evs::schedule(_tickrate, TickCallback, this); +} + +void InterpolatorBase::unregisterTask() +{ + if(_taskstate == 1) + _taskstate = 2; +} + +void InterpolatorBase::waitTask() +{ + switch(_taskstate) + { + case 0: + return; + case 1: + _taskstate = 2; + // fall through + case 2: + while(_taskstate) {} + } +} + +void InterpolatorBase::stop() +{ + unregisterTask(); + // but don't wait +} + +void InterpolatorBase::start() +{ + FGLCD_ASSERT(_taskstate != 1, "interptsk"); + // if task is running, wait until it's gone and + waitTask(); // _taskstate is 0 after this + registerTask(); + _taskstate = 1; +} + +void InterpolatorBase::start(uint16_t tickrate) +{ + _tickrate = tickrate; + start(); +} + +InterpolatorBase::InterpolatorBase(uint16_t tickrate, InterpolatorEntry * mem, uint8_t n) + : _mem(mem), _tickrate(tickrate), _used(0), _cap(n), _taskstate(0) +{ + if(tickrate) + start(tickrate); +} + +void InterpolatorBase::_add(InterpolatorEntry & e) +{ + FGLCD_ASSERT(_used < _cap, "interpad"); // buffer full? + fglcd::NoInterrupt no; + _mem[_used++] = e; +} + +InterpolatorBase::~InterpolatorBase() +{ + unregisterTask(); + waitTask(); +} + +void InterpolatorBase::tick() +{ + const uint8_t n = _used; + for(uint8_t i = 0; i < n; ++i) + { + InterpolatorEntry e = _mem[i]; + e.tickfunc(e.iv); + } +} + +void _InterpolatedFancy::_finishTick() +{ + const Adj oldt = _t; + if(oldt == Adj(-1) && _style == LERP_ONCE) + { + _style = OFF; + return; + } + Adj tt = oldt + _speed; // will overflow if end reached + + if(tt <= oldt) + switch(_style) // overflow! + { + case LERP_ONCE: + tt = Adj(-1); // one last time so we reach the end value + break; + case LERP_SAWTOOTH: + tt = 0; // restart + break; + + } + + _t = tt; +} + +} // end namespace interp diff --git a/democode/src/demolib/interpolator.h b/democode/src/demolib/interpolator.h new file mode 100644 index 0000000..303e898 --- /dev/null +++ b/democode/src/demolib/interpolator.h @@ -0,0 +1,168 @@ +#pragma once + +#include "../demomath/fgmath.h" + +namespace interp { + +typedef uint16_t Adj; + +enum InterpolationStyle +{ + OFF, + LERP_ONCE, + LERP_SAWTOOTH +}; + +template +struct InterpolatedValue +{ + T _val, start, end; + + FORCEINLINE InterpolatedValue() {} + + FORCEINLINE InterpolatedValue(T startval, T endval) + : _val(startval), start(startval), end(endval) + {} + + FORCEINLINE void update(Adj t) + { + _val = lerp(start, end, t); + } + + FORCEINLINE operator T() const { return _val; } +}; + +struct _InterpolatedFancy +{ + volatile Adj _t; + Adj _speed; + volatile uint8_t _style; // InterpolationStyle + void _finishTick(); +}; + +template +struct FancyInterpolatedValue : public InterpolatedValue, public _InterpolatedFancy +{ + typedef FancyInterpolatedValue Self; + + + FancyInterpolatedValue(const T& v = T()) + : InterpolatedValue(v, v) + { + this->_style = OFF; + this->_t = 0; + } + + ~FancyInterpolatedValue() + { + stop(); + } + + void interpolateTo(const T& newval, uint16_t speed, uint8_t style = LERP_ONCE) + { + fglcd::NoInterrupt no; + this->_t = 0; + this->start = this->_val; + this->end = newval; + this->_speed = speed; + this->_style = style; + } + + // TODO, IF NEEDED: interpolateTo() to trigger a user event after a delay + + FORCEINLINE void stop() + { + this->_style = OFF; + } + + FORCEINLINE bool isInterpolating() const + { + return this->_style != OFF; + } + + void tick() + { + if(this->_style == OFF) + return; + this->update(this->_t); + this->_finishTick(); + } + + static NOINLINE void Tick(void *iv) + { + ((Self*)iv)->tick(); + } + + FORCEINLINE T& operator=(const T& v) + { + this->set(v); + return *this; + } + + void set(const T& v) + { + this->stop(); + this->_val = v; + } + +}; + +struct InterpolatorEntry +{ + void (*tickfunc)(void*); + void *iv; +}; + +// Ticks a bunch of registered FancyInterpolatedValue +struct InterpolatorBase +{ + InterpolatorEntry * const _mem; + uint16_t _tickrate; + uint8_t _used; + const uint8_t _cap; + volatile uint8_t _taskstate; // 0:idle, 1:running, 2:exit request + void registerTask(); + void unregisterTask(); + void waitTask(); + + InterpolatorBase(uint16_t tickrate, InterpolatorEntry *mem, uint8_t n); + + static void TickCallback(void*); + void _add(InterpolatorEntry& e); + +public: + + ~InterpolatorBase(); + + void start(); // for automatic, interrupt/event-driven ticking + void start(uint16_t tickrate); + void stop(); + FORCEINLINE void clear() { _used = 0; } + + void tick(); // for manual ticking + + // When adding a thing, that thing must outlive the interpolator! + template + void add(V& iv) + { + InterpolatorEntry e; + e.iv = &iv; + e.tickfunc = V::Tick; + _add(e); + } +}; + +// Supposed to be allocated on the stack. +// Pass as N how many values to hold, max. +// Will assert that the buffer isn't overflowed +template +struct Interpolator : public InterpolatorBase +{ + InterpolatorEntry _entries[N]; + + FORCEINLINE Interpolator(uint16_t tickrate = 0) + : InterpolatorBase(tickrate, &_entries[0], N) + {} +}; + +} // end namespace interp diff --git a/democode/src/demolib/meminfo.h b/democode/src/demolib/meminfo.h new file mode 100644 index 0000000..ee46019 --- /dev/null +++ b/democode/src/demolib/meminfo.h @@ -0,0 +1,72 @@ +#pragma once + +#include "demo_def.h" +#include "scratch.h" + +namespace meminfo { + +#ifdef __AVR__ + +inline constexpr ptrdiff_t RamSize() { return RAMEND - RAMSTART + 1; } +FORCEINLINE uint8_t *RamBegin() { return (uint8_t*)(uintptr_t)RAMSTART; } +FORCEINLINE ptrdiff_t RamAvail(uint8_t *p) +{ + return (RamBegin() + RamSize()) - p; +} +FORCEINLINE ptrdiff_t RamAdvance(uint8_t *& p, uint32_t bytes) +{ + p += (ptrdiff_t)bytes; + return RamAvail(p); +} + +inline constexpr uint32_t ROMSize() { return FLASHEND + 1; } +FORCEINLINE fglcd::FarPtr ROMBegin() { return (fglcd::FarPtr)0; } +FORCEINLINE uint32_t RomAvail(fglcd::FarPtr p) +{ + return (RomBegin() + ROMSize()) - p; +} +FORCEINLINE uint8_t ROMAdvance(fglcd::FarPtr& p, uint32_t bytes) +{ + p += bytes; + return RomAvail(p); +} + +#elif defined(MCU_IS_PC) + +extern "C" int main(int argc, char *argv[]); // some readonly and large enough global symbol + +inline constexpr unsigned RamSize() { return CFG_MAXRAM; } +FORCEINLINE uint8_t *RamBegin() { return reinterpret_cast(&G); } +FORCEINLINE ptrdiff_t RamAvail(uint8_t *p) +{ + return (RamBegin() + sizeof(AllGlobals)) - p; +} +FORCEINLINE ptrdiff_t RamAdvance(uint8_t *& p, uint32_t bytes) +{ + p += (ptrdiff_t)bytes; + if(p >= RamBegin() + sizeof(AllGlobals)) + p = RamBegin(); + return RamAvail(p); +} + +inline constexpr uint32_t ROMSize() { return 0x3FFFF; } +FORCEINLINE fglcd::FarPtr ROMBegin() { return (fglcd::FarPtr)&main; } // whatev +FORCEINLINE uint32_t ROMAvail(fglcd::FarPtr p) +{ + // no idea how large main() is but the entire page is readable so at least 4k on $OS + return (ROMBegin() + 4096) - p; +} +FORCEINLINE uint32_t ROMAdvance(fglcd::FarPtr& p, uint32_t bytes) +{ + fglcd::FarPtr const end = ROMBegin() + 4096; + p += bytes; // must wraparound internally otherwise we'll crash hard + if(p >= end) + p -= 4096; + return ROMAvail(p); +} + +#else +#error unknown platform +#endif + +} // end namespace meminfo diff --git a/democode/src/demolib/music.cpp b/democode/src/demolib/music.cpp new file mode 100644 index 0000000..8fb75b8 --- /dev/null +++ b/democode/src/demolib/music.cpp @@ -0,0 +1,83 @@ +#include "music.h" +#include "demo_def.h" +#include "globals.h" +#include "pmf_player.h" + +#ifdef CFG_ENABLE_AUDIO + +#ifdef CFG_MUSIC_SYNC_CHANNELS +static void TickCallback(void *ud) +{ + musync::UpdateCallbacks(); +} +#endif + +void music_update() +{ +#ifndef MCU_IS_PC + G.mus.player.update(); +#endif +} + +void music_stop() +{ + G.mus.player.stop(); +} + + +bool music_init(const void *data) +{ + if(!G.mus.player.load(data)) + return false; + +#ifdef CFG_MUSIC_SYNC_CHANNELS + G.mus.player.set_tick_callback(TickCallback, NULL); +#endif + return true; +} + +void music_play(bool bg) +{ + G.mus.audiobuf.reset(); + G.mus.player.start(CFG_PMFPLAYER_SAMPLING_RATE); + + if(bg) + { + // FIXME: use timer template on AVR things +#ifdef MCU_IS_PC + // nothing to do +#elif defined(ARDUINO_AVR_MEGA2560) + // Interrupt music updater, uses timer 4 + TCCR4A=0; + TCCR4B=_BV(CS40)|_BV(CS42)|_BV(WGM42); // CTC, /1024 + TCCR4C=0; + TIMSK4=_BV(OCIE4A); + OCR4A=0x10; +#elif defined(ARDUINO_AVR_UNO) + // FIXME: THIS IS PROLLY TOTALLY WRONG + /*TCCR2A=0; + TCCR2B=_BV(CS20)|_BV(CS22)|_BV(WGM22); // CTC, /1024 + TCCR2C=0; + TIMSK2=_BV(OCIE2A); + OCR2A=0x10;*/ +#else +#error dood +#endif + } +} + + +#ifdef __AVR__ +#ifdef ARDUINO_AVR_MEGA2560 +ISR(TIMER4_COMPA_vect, ISR_NOBLOCK) // no blocking -> make sure music keeps playing while updating +#elif ARDUINO_AVR_UNO +ISR(TIMER2_COMPA_vect, ISR_NOBLOCK) +#else +#error dood +#endif +{ + G.mus.player.update(); +} +#endif + +#endif diff --git a/democode/src/demolib/music.h b/democode/src/demolib/music.h new file mode 100644 index 0000000..bd67559 --- /dev/null +++ b/democode/src/demolib/music.h @@ -0,0 +1,28 @@ +#pragma once + +#include "globals.h" + +#ifdef CFG_ENABLE_AUDIO + +FORCEINLINE static int16_t music_curSample16(uint8_t i) +{ + return G.mus.audiobuf.buffer[i]; +} + +bool music_init(const void *data); +void music_update(); +void music_stop(); +void music_play(bool bg); + +#else + +FORCEINLINE static bool music_init(bool bg, const void *data) { return false; } +FORCEINLINE static void music_update() {} +FORCEINLINE static void music_stop() {} + +FORCEINLINE static int16_t music_curSample16(uint8_t i) +{ + return 0; +} + +#endif diff --git a/democode/src/demolib/musicsync.cpp b/democode/src/demolib/musicsync.cpp new file mode 100644 index 0000000..60d8f8c --- /dev/null +++ b/democode/src/demolib/musicsync.cpp @@ -0,0 +1,35 @@ +#include "musicsync.h" +#include "globals.h" + +namespace musync { + +// Run from main thread +void SetChannelCallback(uint8_t channel, ChannelCallbackFunc f, void * ud, CallFilter when) +{ +#ifdef CFG_MUSIC_SYNC_CHANNELS + fglcd::NoInterrupt no; + FGLCD_ASSERT(channel < MaxMusicSyncChannels, "musynccc"); + G.mus.sync[channel].func = f; + G.mus.sync[channel].ud = ud; +#else + FGLCD_ASSERT(0, "musynccc"); +#endif +} + +// Run from music update interrupt +void UpdateCallbacks() +{ +#ifdef CFG_MUSIC_SYNC_CHANNELS + for(uint8_t i = 0; i < MaxMusicSyncChannels; ++i) + { + if(ChannelCallbackFunc f = G.mus.sync[i].func) + { + const pmf_channel_info info = G.mus.player.channel_info(i); + if(info.note_hit || G.mus.sync[i].filter) + f(i, info, G.mus.sync[i].ud); + } + } +#endif +} + +} // end namespace musync diff --git a/democode/src/demolib/musicsync.h b/democode/src/demolib/musicsync.h new file mode 100644 index 0000000..e073af0 --- /dev/null +++ b/democode/src/demolib/musicsync.h @@ -0,0 +1,45 @@ +#pragma once + +#include "demo_def.h" +#include "pmf_player_def.h" + +namespace musync { + +enum CallFilter +{ + CallOnHit = 0, + CallAlways = 1 +}; + +typedef void (*ChannelCallbackFunc)(uint8_t channel, pmf_channel_info info, void *ud); + +struct ChannelCallback +{ + ChannelCallbackFunc func; + void *ud; + uint8_t filter; // CallFilter +}; + +void SetChannelCallback(uint8_t channel, ChannelCallbackFunc f, void *ud, CallFilter when); + +void UpdateCallbacks(); + + +// Helper to temporarily install a sync function +struct Override +{ + const uint8_t channel; + + FORCEINLINE Override(uint8_t channel, ChannelCallbackFunc f, void *ud, CallFilter when) + : channel(channel) + { + SetChannelCallback(channel, f, ud, when); + } + + FORCEINLINE ~Override() + { + SetChannelCallback(channel, NULL, NULL, CallOnHit); + } +}; + +} // end namespace musync diff --git a/democode/src/demolib/musicsyncvar.cpp b/democode/src/demolib/musicsyncvar.cpp new file mode 100644 index 0000000..d76d5d0 --- /dev/null +++ b/democode/src/demolib/musicsyncvar.cpp @@ -0,0 +1 @@ +#include "musicsyncvar.h" diff --git a/democode/src/demolib/musicsyncvar.h b/democode/src/demolib/musicsyncvar.h new file mode 100644 index 0000000..eafffab --- /dev/null +++ b/democode/src/demolib/musicsyncvar.h @@ -0,0 +1,39 @@ +#pragma once + +// Helper to sync a variable with the music +// Intention: Whenever a note hits, set the value to peak, +// then (slowly) decay back to base. + +#include "interpolator.h" +#include "musicsync.h" + +template +class MusicSyncVar : public interp::FancyInterpolatedValue, public musync::Override +{ + typedef MusicSyncVar Self; +public: + T base; + T peak; + uint16_t speed; + + typedef interp::FancyInterpolatedValue IVal; + MusicSyncVar(interp::InterpolatorBase& inp, uint8_t channel, T base_, T peak_, uint16_t speed_) + : IVal(base_) + , musync::Override(channel, s_hit, this, musync::CallOnHit) + , base(base_), peak(peak_), speed(speed_) + { + inp.add(*this); + } + + void hit() + { + this->set(peak); + this->interpolateTo(base, this->speed); + } + +private: + static void s_hit(uint8_t channel, pmf_channel_info info, void *ud) + { + ((Self*)ud)->hit(); + } +}; diff --git a/democode/src/demolib/packdef.h b/democode/src/demolib/packdef.h new file mode 100644 index 0000000..1e7e10b --- /dev/null +++ b/democode/src/demolib/packdef.h @@ -0,0 +1,56 @@ +#pragma once + +enum PackType +{ + PK_FAIL = -1, + // lowlevel algos + PK_TSCHUNK = 0, + PK_SRLE, + PK_UNCOMP, + PK_RLE, + PK_EXO, + // higher level composite algos come afterwards + PK_BLOCK_TSCHUNK, + PK_BLOCK_MIX, + PK_BLOCK_EXO, + PK_BLOCK_RLE, +}; + +#define PK_NUM_BASIC_ALGOS 5 + +static const char * const PackTypeName[] = +{ + "PK_TSCHUNK", + "PK_SRLE", + "PK_UNCOMP", + "PK_RLE", + "PK_EXO", + "PK_BLOCK_TSCHUNK", + "PK_BLOCK_MIX", + "PK_BLOCK_EXO", + "PK_BLOCK_RLE" +}; + +static const char * const PackTypeNameShort[] = { "tsc", "srl", "raw", "rle", "exo", "Bt", "Bm", "Be", "Br" }; + +enum PackTypeProps_ // bitmask +{ + PP_HAS_SIZE = 1, // needs size externally supplied, aka has no end marker + PP_COMPOUND = 2, // method combines other method + PP_HAS_WINDOW = 4, // depacker needs memory window and that window size is relevant for compression + PP_NON_RELOCATABLE = 8, // depacker depends on previously packed content. compressed data may not be relocatable. +}; + +static constexpr unsigned PackTypeProps[] = +{ + PP_HAS_SIZE | PP_NON_RELOCATABLE, + PP_HAS_SIZE | PP_NON_RELOCATABLE, + PP_HAS_SIZE, + 0, + PP_HAS_WINDOW, + + PP_COMPOUND, + PP_COMPOUND, + PP_COMPOUND, + PP_COMPOUND +}; diff --git a/democode/src/demolib/pmf_data.h b/democode/src/demolib/pmf_data.h new file mode 100644 index 0000000..1c69f9a --- /dev/null +++ b/democode/src/demolib/pmf_data.h @@ -0,0 +1,115 @@ +//============================================================================ +// PMF Player +// +// Copyright (c) 2019, Profoundic Technologies, Inc. +// All rights reserved. +//---------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Profoundic Technologies nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL PROFOUNDIC TECHNOLOGIES BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//============================================================================ + +#ifndef PFC_PMF_DATA_H +#define PFC_PMF_DATA_H +//--------------------------------------------------------------------------- + + +//============================================================================ +// interface +//============================================================================ +// external +#include +#include + +// new +struct pmf_instrument_header; +#define PFC_OFFSETOF(type__, mvar__) offsetof(type__, mvar__) +//--------------------------------------------------------------------------- + + +//============================================================================ +// pmf_sample_header +//============================================================================ +struct pmf_sample_header +{ + uint32_t data_offset; + uint32_t length; + uint32_t loop_length_and_panning; + int16_t finetune; + uint8_t flags; + uint8_t volume; +}; +//---------------------------------------------------------------------------- + + +//============================================================================ +// pmf_instrument_header +//============================================================================ +struct pmf_instrument_header +{ + uint16_t sample_idx; // sample index or offset to a note map + uint16_t vol_env_offset; + uint16_t pitch_env_offset; + uint16_t fadeout_speed; + uint8_t volume; + int8_t panning; +}; +//---------------------------------------------------------------------------- + + +//============================================================================ +// e_pmf_sample_flags +//============================================================================ +enum e_pmf_sample_flags +{ + pmfsmpflag_16bit = 0x01, + pmfsmpflag_bidi_loop = 0x02, +}; +//---------------------------------------------------------------------------- + + +//============================================================================ +// sample data offsets +//============================================================================ +enum {pmfcfg_sample_metadata_size=sizeof(pmf_sample_header)}; +enum {pmfcfg_offset_smp_data=PFC_OFFSETOF(pmf_sample_header, data_offset)}; +enum {pmfcfg_offset_smp_length=PFC_OFFSETOF(pmf_sample_header, length)}; +enum {pmfcfg_offset_smp_loop_length_and_panning=PFC_OFFSETOF(pmf_sample_header, loop_length_and_panning)}; +enum {pmfcfg_offset_smp_finetune=PFC_OFFSETOF(pmf_sample_header, finetune)}; +enum {pmfcfg_offset_smp_flags=PFC_OFFSETOF(pmf_sample_header, flags)}; +enum {pmfcfg_offset_smp_volume=PFC_OFFSETOF(pmf_sample_header, volume)}; +//---------------------------------------------------------------------------- + + +//============================================================================ +// instrument data offsets +//============================================================================ +enum {pmfcfg_instrument_metadata_size=sizeof(pmf_instrument_header)}; +enum {pmfcfg_offset_inst_sample_idx=PFC_OFFSETOF(pmf_instrument_header, sample_idx)}; +enum {pmfcfg_offset_inst_vol_env=PFC_OFFSETOF(pmf_instrument_header, vol_env_offset)}; +enum {pmfcfg_offset_inst_pitch_env=PFC_OFFSETOF(pmf_instrument_header, pitch_env_offset)}; +enum {pmfcfg_offset_inst_fadeout_speed=PFC_OFFSETOF(pmf_instrument_header, fadeout_speed)}; +enum {pmfcfg_offset_inst_volume=PFC_OFFSETOF(pmf_instrument_header, volume)}; +enum {pmfcfg_offset_inst_panning=PFC_OFFSETOF(pmf_instrument_header, panning)}; +//---------------------------------------------------------------------------- + +//============================================================================ +#endif diff --git a/democode/src/demolib/pmf_player.cpp b/democode/src/demolib/pmf_player.cpp new file mode 100644 index 0000000..ce7c10b --- /dev/null +++ b/democode/src/demolib/pmf_player.cpp @@ -0,0 +1,1363 @@ +//============================================================================ +// PMF Player +// +// Copyright (c) 2019, Profoundic Technologies, Inc. +// All rights reserved. +//---------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Profoundic Technologies nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL PROFOUNDIC TECHNOLOGIES BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//============================================================================ + +#include "pmf_player.h" +//--------------------------------------------------------------------------- + + +//============================================================================ +// pmf_header +//============================================================================ +struct pmf_header +{ + char signature[4]; + uint16_t version; + uint16_t flags; // e_pmf_flags + uint32_t file_size; + uint32_t sample_meta_offs; + uint32_t instrument_meta_offs; + uint32_t pattern_meta_offs; + uint32_t env_data_offs; + uint32_t nmap_data_offs; + uint32_t track_data_offs; + uint8_t initial_speed; + uint8_t initial_tempo; + uint16_t note_period_min; + uint16_t note_period_max; + uint16_t playlist_length; + uint8_t num_channels; + uint8_t num_patterns; + uint8_t num_instruments; + uint8_t num_samples; + uint8_t first_playlist_entry; +}; +//---------------------------------------------------------------------------- + + +//=========================================================================== +// PMF format config +//=========================================================================== +// PMF config +enum {pmf_file_version=0x1400}; // v1.4 +// PMF file structure +enum {pmfcfg_offset_signature=PFC_OFFSETOF(pmf_header, signature)}; +enum {pmfcfg_offset_version=PFC_OFFSETOF(pmf_header, version)}; +enum {pmfcfg_offset_flags=PFC_OFFSETOF(pmf_header, flags)}; +enum {pmfcfg_offset_file_size=PFC_OFFSETOF(pmf_header, file_size)}; +enum {pmfcfg_offset_smp_meta_offs=PFC_OFFSETOF(pmf_header, sample_meta_offs)}; +enum {pmfcfg_offset_inst_meta_offs=PFC_OFFSETOF(pmf_header, instrument_meta_offs)}; +enum {pmfcfg_offset_pat_meta_offs=PFC_OFFSETOF(pmf_header, pattern_meta_offs)}; +enum {pmfcfg_offset_env_data_offs=PFC_OFFSETOF(pmf_header, env_data_offs)}; +enum {pmfcfg_offset_nmap_data_offs=PFC_OFFSETOF(pmf_header, nmap_data_offs)}; +enum {pmfcfg_offset_track_data_offs=PFC_OFFSETOF(pmf_header, track_data_offs)}; +enum {pmfcfg_offset_init_speed=PFC_OFFSETOF(pmf_header, initial_speed)}; +enum {pmfcfg_offset_init_tempo=PFC_OFFSETOF(pmf_header, initial_tempo)}; +enum {pmfcfg_offset_note_period_min=PFC_OFFSETOF(pmf_header, note_period_min)}; +enum {pmfcfg_offset_note_period_max=PFC_OFFSETOF(pmf_header, note_period_max)}; +enum {pmfcfg_offset_playlist_length=PFC_OFFSETOF(pmf_header, playlist_length)}; +enum {pmfcfg_offset_num_channels=PFC_OFFSETOF(pmf_header, num_channels)}; +enum {pmfcfg_offset_num_patterns=PFC_OFFSETOF(pmf_header, num_patterns)}; +enum {pmfcfg_offset_num_instruments=PFC_OFFSETOF(pmf_header, num_instruments)}; +enum {pmfcfg_offset_num_samples=PFC_OFFSETOF(pmf_header, num_samples)}; +enum {pmfcfg_offset_playlist=PFC_OFFSETOF(pmf_header, first_playlist_entry)}; +enum {pmfcfg_pattern_metadata_header_size=2}; +enum {pmfcfg_pattern_metadata_track_offset_size=2}; +enum {pmfcfg_offset_pattern_metadata_last_row=0}; +enum {pmfcfg_offset_pattern_metadata_track_offsets=2}; +// envelope configs +enum {pmfcfg_offset_env_num_points=0}; +enum {pmfcfg_offset_env_loop_start=1}; +enum {pmfcfg_offset_env_loop_end=2}; +enum {pmfcfg_offset_env_sustain_loop_start=3}; +enum {pmfcfg_offset_env_sustain_loop_end=4}; +enum {pmfcfg_offset_env_points=6}; +enum {pmfcfg_envelope_point_size=4}; +enum {pmfcfg_offset_env_point_tick=0}; +enum {pmfcfg_offset_env_point_val=2}; +// note map config +enum {pmfcfg_max_note_map_regions=8}; +enum {pmfcfg_offset_nmap_num_entries=0}; +enum {pmfcfg_offset_nmap_entries=1}; +enum {pmfcfg_nmap_entry_size_direct=2}; +enum {pmfcfg_nmap_entry_size_range=3}; +enum {pmgcfg_offset_nmap_entry_note_idx_offs=0}; +enum {pmgcfg_offset_nmap_entry_sample_idx=1}; +// bit-compression settings +enum {pmfcfg_num_data_mask_bits=4}; +enum {pmfcfg_num_note_bits=7}; // max 10 octaves (0-9) (12*10=120) +enum {pmfcfg_num_instrument_bits=6}; // max 64 instruments +enum {pmfcfg_num_volume_bits=6}; // volume range [0, 63] +enum {pmfcfg_num_effect_bits=4}; // effects 0-15 +enum {pmfcfg_num_effect_data_bits=8}; +// PMF flags +enum e_pmf_flags +{ + pmfflag_linear_freq_table =0x01, // 0=Amiga, 1=linear +}; +// PMF special notes +enum {pmfcfg_note_cut=120}; +enum {pmfcfg_note_off=121}; +// PMF effects +enum {num_subfx_value_bits=4}; +enum {subfx_value_mask=~(unsigned(-1)<>bit_pos_; + bit_pos_+=num_bits_; + if(bit_pos_>7) + { + ++ptr_; + if(bit_pos_-=8) + v|=pgm_read_byte(ptr_)<<(num_bits_-bit_pos_); + } + return v; + } + //------------------------------------------------------------------------- + + //========================================================================= + // fast_exp2 + //========================================================================= + float fast_exp2(float x_) + { + // map x_ to range [0, 0.5] + int adjustment=0; + uint8_t int_arg=uint8_t(x_); + x_-=int_arg; + if(x_>0.5f) + { + adjustment=1; + x_-=0.5f; + } + + // calculate 2^x_ approximation + float x2=x_*x_; + float q=20.8189237930062f+x2; + float x_p=x_*(7.2152891521493f+0.0576900723731f*x2); + float res=(1< +//--------------------------------------------------------------------------- + + +//=========================================================================== +// pmf_player +//=========================================================================== +pmf_player::pmf_player() +{ + m_pmf_file=0; + m_sampling_freq=0; + m_row_callback=0; + m_tick_callback=0; + m_speed=0; +} +//---- + +pmf_player::~pmf_player() +{ + stop(); +} +//---- + +bool pmf_player::load(const void *pmem_pmf_file_) +{ + // check for valid PMF file + const uint8_t *pmf_file=static_cast(pmem_pmf_file_); + if(pgm_read_dword(pmf_file+pmfcfg_offset_signature)!=0x78666d70) + { + PMF_SERIAL_LOG("Error: Invalid PMF file. Please use pmf_converter to generate the file.\r\n"); + return false; + } + if((pgm_read_word(pmf_file+pmfcfg_offset_version)&0xfff0)!=pmf_file_version) + { + PMF_SERIAL_LOG("Error: PMF file version mismatch. Please use matching pmf_converter to generate the file.\r\n"); + return false; + } + + // read PMF properties + m_pmf_file=pmf_file; + m_num_pattern_channels=pgm_read_byte(m_pmf_file+pmfcfg_offset_num_channels); + m_num_instruments=pgm_read_byte(m_pmf_file+pmfcfg_offset_num_instruments); + m_num_samples=pgm_read_byte(m_pmf_file+pmfcfg_offset_num_samples); + enable_playback_channels(m_num_pattern_channels); + m_pmf_flags=pgm_read_word(m_pmf_file+pmfcfg_offset_flags); + m_note_slide_speed=m_pmf_flags&pmfflag_linear_freq_table?4:2; + PMF_SERIAL_LOG("PMF file loaded (%i channels)\r\n", m_num_pattern_channels); + return true; +} +//---- + +void pmf_player::enable_playback_channels(uint8_t num_channels_) +{ + if(m_pmf_file) + m_num_playback_channels=num_channels_255?255:v; +} +//---- + +void pmf_player::apply_channel_effect_note_slide(audio_channel &chl_) +{ + // slide note period towards target period and clamp the period to target + if(!chl_.sample_speed) + return; + int16_t note_period=chl_.note_period; + int16_t note_target_prd=chl_.fxmem_note_slide_prd; + int16_t slide_spd=(note_period0)^(note_periodm_note_period_max?0:get_sample_speed(chl_.note_period, chl_.sample_speed>=0); +} +//---- + +void pmf_player::apply_channel_effect_vibrato(audio_channel &chl_) +{ + if(!chl_.sample_speed) + return; + uint8_t wave_idx=chl_.fxmem_vibrato_wave&3; + int8_t vibrato_pos=chl_.fxmem_vibrato_pos; + int8_t wave_sample=vibrato_pos<0?-int8_t(pgm_read_byte(&s_waveforms[wave_idx][~vibrato_pos])):pgm_read_byte(&s_waveforms[wave_idx][vibrato_pos]); + chl_.sample_speed=get_sample_speed(chl_.note_period+(int16_t(wave_sample*chl_.fxmem_vibrato_depth)>>8), chl_.sample_speed>=0); + if((chl_.fxmem_vibrato_pos+=chl_.fxmem_vibrato_spd)>31) + chl_.fxmem_vibrato_pos-=64; +} +//---- + +void pmf_player::apply_channel_effects() +{ + if(++m_arpeggio_counter==3) + m_arpeggio_counter=0; + for(unsigned ci=0; ci>(4*m_arpeggio_counter))&0xf), chl.sample_finetune); + chl.sample_speed=get_sample_speed(note_period, chl.sample_speed>=0); + } break; + + case pmffx_note_slide: apply_channel_effect_note_slide(chl); break; + + case pmffx_note_vol_slide: + { + if(chl.fxmem_note_slide_spd<0xe0) + apply_channel_effect_note_slide(chl); + if(!(chl.fxmem_vol_slide_spd&pmffx_volsldtype_fine_mask)) + apply_channel_effect_volume_slide(chl); + } break; + + case pmffx_volume_slide: apply_channel_effect_volume_slide(chl); break; + + case pmffx_vibrato: apply_channel_effect_vibrato(chl); break; + + case pmffx_vibrato_vol_slide: + { + apply_channel_effect_vibrato(chl); + if(!(chl.fxmem_vol_slide_spd&pmffx_volsldtype_fine_mask)) + apply_channel_effect_volume_slide(chl); + } break; + + case pmffx_retrig_vol_slide: + { + if(!--chl.fxmem_retrig_count) + { + uint8_t effect_data=chl.effect_data; + chl.fxmem_retrig_count=effect_data&0xf; + int vol=chl.sample_volume; + effect_data>>=4; + switch(effect_data) + { + case 6: vol=(vol+vol)/3; break; + case 7: vol>>=1; break; + case 14: vol=(vol*3)/2; break; + case 15: vol+=vol; break; + default: + { + uint8_t delta=2<<(effect_data&7); + vol+=effect_data&8?+delta:-delta; + } + } + chl.sample_volume=vol<0?0:vol>255?255:vol; + chl.sample_pos=0; + chl.note_hit=1; + } + } break; + + case pmffx_subfx|(pmfsubfx_note_cut<255?255:v; + return false; + } + return true; +} +//---- + +bool pmf_player::init_effect_note_slide(audio_channel &chl_, uint8_t slide_speed_, uint16_t target_note_period_) +{ + // update note slide effect memory and check for regular note slide + if(slide_speed_) + chl_.fxmem_note_slide_spd=slide_speed_; + else + slide_speed_=chl_.fxmem_note_slide_spd; + if(target_note_period_) + chl_.fxmem_note_slide_prd=target_note_period_; + if(slide_speed_<0xe0) + return true; + + // apply fine/extra-fine note slide + if(!chl_.sample_speed) + return false; + int16_t note_period=chl_.note_period; + int16_t slide_spd=slide_speed_>=0xf0?(slide_speed_-0xf0)*4:(slide_speed_-0xe0); + if(note_period>target_note_period_) + slide_spd=-slide_spd; + note_period+=slide_spd; + if((slide_spd>0)^(note_periodm_note_period_max) + chl_.sample_speed=0; + return false; +} +//---- + +void pmf_player::init_effect_vibrato(audio_channel &chl_, uint8_t vibrato_depth_, uint8_t vibrato_speed_) +{ + // update vibrato attributes + if(vibrato_depth_) + chl_.fxmem_vibrato_depth=vibrato_depth_<<3; + if(vibrato_speed_) + chl_.fxmem_vibrato_spd=vibrato_speed_; +} +//---- + +void pmf_player::evaluate_envelope(envelope_state &env_, uint16_t env_data_offs_, bool is_note_off_) +{ + // advance envelope (check if passes the current span end point) + const uint8_t *envelope=m_pmf_file+pgm_read_dword(m_pmf_file+pmfcfg_offset_env_data_offs)+env_data_offs_; + const uint8_t *env_span_data=envelope+pmfcfg_offset_env_points+env_.pos*pmfcfg_envelope_point_size; + uint16_t env_span_tick_end=pgm_read_word(env_span_data+pmfcfg_envelope_point_size+pmfcfg_offset_env_point_tick); + if(++env_.tick>=env_span_tick_end) + { + // get envelope start and end points (sustain/loop/none) + uint8_t env_pnt_start_idx, env_pnt_end_idx; + if(is_note_off_) + { + env_pnt_start_idx=pgm_read_byte(envelope+pmfcfg_offset_env_loop_start); + env_pnt_end_idx=pgm_read_byte(envelope+pmfcfg_offset_env_loop_end); + } + else + { + env_pnt_start_idx=pgm_read_byte(envelope+pmfcfg_offset_env_sustain_loop_start); + env_pnt_end_idx=pgm_read_byte(envelope+pmfcfg_offset_env_sustain_loop_end); + } + uint8_t env_last_pnt_idx=pgm_read_byte(envelope+pmfcfg_offset_env_num_points)-1; + env_pnt_start_idx=min(env_pnt_start_idx, env_last_pnt_idx); + env_pnt_end_idx=min(env_pnt_end_idx, env_last_pnt_idx); + + // check for envelope end/loop-end + if(++env_.pos==env_pnt_end_idx) + { + if(env_pnt_start_idx>8)*(chl.vol_fadeout>>8); + uint16_t fadeout_speed=pgm_read_word(chl.inst_metadata+pmfcfg_offset_inst_fadeout_speed); + chl.vol_fadeout=chl.vol_fadeout>fadeout_speed?chl.vol_fadeout-fadeout_speed:0; + } + } +} +//---------------------------------------------------------------------------- + +uint16_t pmf_player::get_note_period(uint8_t note_idx_, int16_t finetune_) +{ + if(m_pmf_flags&pmfflag_linear_freq_table) + return 7680-note_idx_*64-finetune_/2; + return uint16_t(27392.0f/fast_exp2((note_idx_*128+finetune_)/(12.0f*128.0f))+0.5f); +} +//---- + +int16_t pmf_player::get_sample_speed(uint16_t note_period_, bool forward_) +{ + int16_t speed; + if(m_pmf_flags&pmfflag_linear_freq_table) + speed=int16_t((8363.0f*8.0f/m_sampling_freq)*fast_exp2(float(7680-note_period_)/768.0f)+0.5f); + else + speed=int16_t((7093789.2f*256.0f/m_sampling_freq)/note_period_+0.5f); + return forward_?speed:-speed; +} +//---- + +void pmf_player::set_instrument(audio_channel &chl_, uint8_t inst_idx_, uint8_t note_idx_) +{ + uint8_t inst_vol=0xff; + int8_t panning=-128; + if(m_num_instruments) + { + // setup instrument metadata and check for note mapping + const uint8_t *inst_metadata=m_pmf_file+pgm_read_dword(m_pmf_file+pmfcfg_offset_inst_meta_offs)+inst_idx_*pmfcfg_instrument_metadata_size; + chl_.inst_metadata=inst_metadata; + inst_vol=pgm_read_byte(inst_metadata+pmfcfg_offset_inst_volume); + panning=pgm_read_byte(inst_metadata+pmfcfg_offset_inst_panning); + uint16_t sample_idx=pgm_read_word(inst_metadata+pmfcfg_offset_inst_sample_idx); + uint8_t note_idx_offs=0; + if(sample_idx>=m_num_samples) + { + // access note map and check for range vs direct map + uint8_t nidx=note_idx_!=0xff?note_idx_:chl_.base_note_idx&127; + const uint8_t *nmap=m_pmf_file+pgm_read_dword(m_pmf_file+pmfcfg_offset_nmap_data_offs)+sample_idx-m_num_samples; + uint8_t num_entries=pgm_read_byte(nmap+pmfcfg_offset_nmap_num_entries); + if(num_entries<120) + { + // find note map range for given note index + nmap+=pmfcfg_offset_nmap_entries; + while(true) + { + uint8_t range_max=pgm_read_byte(nmap); + if(nidx<=range_max) + { + ++nmap; + break; + } + nmap+=pmfcfg_nmap_entry_size_range; + } + } + else + nmap+=pmfcfg_offset_nmap_entries+nidx*pmfcfg_nmap_entry_size_direct; + + // get note offset and sample index + note_idx_offs=pgm_read_byte(nmap+pmgcfg_offset_nmap_entry_note_idx_offs); + sample_idx=pgm_read_byte(nmap+pmgcfg_offset_nmap_entry_sample_idx); + } + chl_.inst_note_idx_offs=note_idx_offs; + inst_idx_=uint8_t(sample_idx); + } + + // update sample for the channel + const uint8_t *smp_metadata=m_pmf_file+pgm_read_dword(m_pmf_file+pmfcfg_offset_smp_meta_offs)+inst_idx_*pmfcfg_sample_metadata_size; + if(chl_.smp_metadata!=smp_metadata) + { + chl_.sample_pos=0; + if(chl_.sample_speed) + chl_.sample_speed=get_sample_speed(chl_.note_period, true); + } + chl_.smp_metadata=smp_metadata; + chl_.sample_volume=(uint16_t(inst_vol)*uint16_t(pgm_read_byte(chl_.smp_metadata+pmfcfg_offset_smp_volume)))>>8; + chl_.sample_finetune=pgm_read_word(chl_.smp_metadata+pmfcfg_offset_smp_finetune); + + // setup panning + if(panning==-128) + panning=pgm_read_byte(smp_metadata+pmfcfg_offset_smp_loop_length_and_panning+3); + if(panning!=-128) + chl_.sample_panning=panning; +} +//---- + +void pmf_player::hit_note(audio_channel &chl_, uint8_t note_idx_, uint8_t sample_start_pos_, bool reset_sample_pos_) +{ + if(!chl_.smp_metadata) + return; + chl_.note_period=get_note_period(note_idx_, chl_.sample_finetune); + chl_.base_note_idx=note_idx_; + if(reset_sample_pos_) + chl_.sample_pos=sample_start_pos_*65536; + chl_.sample_speed=get_sample_speed(chl_.note_period, true); + chl_.note_hit=reset_sample_pos_; + if(!(chl_.fxmem_vibrato_wave&0x4)) + chl_.fxmem_vibrato_pos=0; +} +//---- + +void pmf_player::process_pattern_row() +{ + // store current track positions + const uint8_t *current_track_poss[pmfplayer_max_channels]; + uint8_t current_track_bit_poss[pmfplayer_max_channels]; + for(uint8_t ci=0; ci>4); + else + { + // initialize volume effect + uint8_t volfx_data=volume&0xf; + switch(volume&0xf0) + { + // volume slide + case pmfvolfx_vol_slide_down: + case pmfvolfx_vol_slide_up: + case pmfvolfx_vol_slide_fine_down: + case pmfvolfx_vol_slide_fine_up: + { + if(init_effect_volume_slide(chl, volume&0x3f)) + chl.vol_effect=pmfvolfx_vol_slide; + } break; + + // note slide down + case pmfvolfx_note_slide_down: + { + // init note slide down and disable note retrigger + if(init_effect_note_slide(chl, volfx_data, note_slide_down_target_period)) + chl.vol_effect=pmfvolfx_note_slide; + } break; + + // note slide up + case pmfvolfx_note_slide_up: + { + // init note slide up and disable note retrigger + if(init_effect_note_slide(chl, volfx_data, note_slide_up_target_period)) + chl.vol_effect=pmfvolfx_note_slide; + } break; + + // note slide + case pmfvolfx_note_slide: + { + // init note slide and disable note retrigger + if(init_effect_note_slide(chl, volfx_data, note_idx!=0xff?get_note_period(note_idx, chl.sample_finetune):0)) + chl.vol_effect=pmfvolfx_note_slide; + note_idx=0xff; + } break; + + // set vibrato speed + case pmfvolfx_set_vibrato_speed: + { + if(volfx_data) + chl.fxmem_vibrato_spd=volfx_data; + } break; + + // vibrato + case pmfvolfx_vibrato: + { + init_effect_vibrato(chl, volfx_data, 0); + chl.vol_effect=pmfvolfx_vibrato; + update_sample_speed=false; + } break; + + // set panning + case pmfvolfx_set_panning: + { + chl.sample_panning=volfx_data?(volfx_data|(volfx_data<<4))-128:-127; + } break; + + // fine panning slide left + case pmfvolfx_pan_slide_fine_left: + { + if(chl.sample_panning==-128) + break; + chl.sample_panning=int8_t(max(-127, int(chl.sample_panning)-volfx_data*4)); + } break; + + // fine panning slide right + case pmfvolfx_pan_slide_fine_right: + { + if(chl.sample_panning==-128) + break; + chl.sample_panning=int8_t(min(127, int(chl.sample_panning)+volfx_data*4)); + } break; + } + } + } + + // get effect + chl.effect=0xff; + if(effect!=0xff) + { + // setup effect + switch(effect) + { + case pmffx_set_speed_tempo: + { + if(effect_data<32) + m_speed=effect_data; + else + m_num_batch_samples=(m_sampling_freq*125)/long(effect_data*50); + } break; + + case pmffx_position_jump: + { + m_current_pattern_playlist_pos=effect_data-1; + m_current_pattern_row_idx=m_current_pattern_last_row; + } break; + + case pmffx_pattern_break: + { + m_current_pattern_row_idx=m_current_pattern_last_row; + num_skip_rows=effect_data; + } break; + + case pmffx_volume_slide: + { + if(init_effect_volume_slide(chl, effect_data)) + chl.effect=pmffx_volume_slide; + } break; + + case pmffx_note_slide_down: + { + if(init_effect_note_slide(chl, effect_data, note_slide_down_target_period)) + chl.effect=pmffx_note_slide; + } break; + + case pmffx_note_slide_up: + { + if(init_effect_note_slide(chl, effect_data, note_slide_up_target_period)) + chl.effect=pmffx_note_slide; + } break; + + case pmffx_note_slide: + { + // init note slide and disable retrigger + if(init_effect_note_slide(chl, effect_data, note_idx!=0xff?get_note_period(note_idx, chl.sample_finetune):0)) + chl.effect=pmffx_note_slide; + note_idx=0xff; + } break; + + case pmffx_arpeggio: + { + chl.effect=pmffx_arpeggio; + if(effect_data) + chl.fxmem_arpeggio=effect_data; + } break; + + case pmffx_vibrato: + { + // update vibrato attributes + init_effect_vibrato(chl, effect_data&0x0f, effect_data>>4); + chl.effect=pmffx_vibrato; + update_sample_speed=false; + } break; + + case pmffx_tremolo: + { + /*todo*/ + } break; + + case pmffx_note_vol_slide: + { + init_effect_note_slide(chl, 0, note_idx!=0xff?get_note_period(note_idx, chl.sample_finetune):0); + init_effect_volume_slide(chl, effect_data); + chl.effect=pmffx_note_vol_slide; + note_idx=0xff; + } break; + + case pmffx_vibrato_vol_slide: + { + init_effect_vibrato(chl, 0, 0); + init_effect_volume_slide(chl, effect_data); + chl.effect=pmffx_vibrato_vol_slide; + update_sample_speed=false; + } break; + + case pmffx_retrig_vol_slide: + { + chl.sample_pos=0; + chl.fxmem_retrig_count=effect_data&0xf; + chl.effect=pmffx_retrig_vol_slide; + chl.effect_data=effect_data; + chl.note_hit=1; + } break; + + case pmffx_set_sample_offset: + { + sample_start_pos=effect_data; + chl.sample_pos=effect_data*long(65536); + } break; + + case pmffx_subfx: + { + switch(effect_data>>4) + { + case pmfsubfx_set_glissando: + { + /*todo*/ + } break; + + case pmfsubfx_set_finetune: + { + if(inst_idx!=0xff) + { + /*todo: should move C4hz values of instruments to RAM*/ + } + } break; + + case pmfsubfx_set_vibrato_wave: + { + uint8_t wave=effect_data&3; + chl.fxmem_vibrato_wave=(wave<3?wave:m_batch_pos%3)|(effect_data&4); + } break; + + case pmfsubfx_set_tremolo_wave: + { + /*todo*/ + } break; + + case pmfsubfx_pattern_delay: + { + m_pattern_delay=(effect_data&0xf)+1; + } break; + + case pmfsubfx_pattern_loop: + { + effect_data&=0xf; + if(effect_data) + { + if(m_pattern_loop_cnt) + --m_pattern_loop_cnt; + else + m_pattern_loop_cnt=effect_data; + if(m_pattern_loop_cnt) + loop_pattern=true; + } + else + { + // set loop start + m_pattern_loop_row_idx=m_current_pattern_row_idx; + for(unsigned ci=0; ci=0); + } + + // check for pattern loop + if(loop_pattern) + { + for(unsigned ci=0; ci + +//=========================================================================== +// logging +//=========================================================================== +#if PMF_USE_SERIAL_LOGS==1 +#define PMF_SERIAL_LOG(...) {char buf[64]; sprintf(buf, __VA_ARGS__); Serial.print(buf);} +#else +#define PMF_SERIAL_LOG(...) +#endif +//--------------------------------------------------------------------------- + + +template +void pmf_player::mix_buffer_impl(pmf_mixer_buffer &buf_, unsigned num_samples_) +{ + T * const bufstart=(T*)buf_.begin, * const buffer_end=bufstart+num_samples_*(stereo?2:1); + memset(bufstart, 0, (buffer_end - bufstart) * sizeof(T)); + audio_channel *channel=m_channels, *channel_end=channel+m_num_playback_channels; + do + { + // check for active channel + if(!channel->sample_speed) + continue; + + // get channel attributes + size_t sample_addr=(size_t)(m_pmf_file+pgm_read_dword(channel->smp_metadata+pmfcfg_offset_smp_data)); + uint32_t sample_pos=channel->sample_pos; + int16_t sample_speed=channel->sample_speed; + uint32_t sample_end=uint32_t(pgm_read_dword(channel->smp_metadata+pmfcfg_offset_smp_length))<<8; + uint32_t sample_loop_len=(pgm_read_dword(channel->smp_metadata+pmfcfg_offset_smp_loop_length_and_panning)&0xffffff)<<8; + uint8_t sample_volume=(channel->sample_volume*(channel->vol_env.value>>8))>>8; + uint32_t sample_pos_offs=sample_end-sample_loop_len; + if(sample_pos>8; + sample_pos-=sample_pos_offs; + sample_end-=sample_pos_offs; + + // setup panning + int8_t panning=channel->sample_panning; + int16_t sample_phase_shift=panning==-128?0xffff:0; + panning&=~int8_t(sample_phase_shift); + uint8_t sample_volume_l=uint8_t((uint16_t(sample_volume)*uint8_t(128-panning))>>8); + uint8_t sample_volume_r=uint8_t((uint16_t(sample_volume)*uint8_t(128+panning))>>8); + + // mix channel to the buffer + T *buf=bufstart; + do + { + // get sample data and adjust volume +#if PMF_USE_LINEAR_INTERPOLATION==1 + uint16_t smp_data=((uint16_t)pgm_read_word(sample_addr+(sample_pos>>8))); + uint8_t sample_pos_frc=sample_pos&255; + int16_t smp=((int16_t(int8_t(smp_data&255))*(256-sample_pos_frc))>>8)+((int16_t(int8_t(smp_data>>8))*sample_pos_frc)>>8); +#else + int16_t smp=(int8_t)pgm_read_byte(sample_addr+(sample_pos>>8)); +#endif + + // mix the result to the audio buffer (the if-branch with compile-time constant will be optimized out) + if(stereo) + { + (*buf++)+=T(sample_volume_l*smp)>>(16-channel_bits); + (*buf++)+=T(sample_volume_r*(smp^sample_phase_shift))>>(16-channel_bits); + } + else + (*buf++)+=T(sample_volume*smp)>>(16-channel_bits); + + // advance sample position + sample_pos+=sample_speed; + if(sample_pos>=sample_end) + { + // check for loop + if(!sample_loop_len) + { + channel->sample_speed=0; + break; + } + + // apply normal/bidi loop + if(pgm_read_byte(channel->smp_metadata+pmfcfg_offset_smp_flags)&pmfsmpflag_bidi_loop) + { + sample_pos-=sample_speed*2; + channel->sample_speed=sample_speed=-sample_speed; + } + else + sample_pos-=sample_loop_len; + } + } while(bufsample_pos=sample_pos+sample_pos_offs; + } while(++channel!=channel_end); + + // advance buffer + ((T*&)buf_.begin)+=num_samples_*(stereo?2:1); + buf_.num_samples-=num_samples_; +} +//--------------------------------------------------------------------------- + +template +void pmf_audio_buffer::reset() +{ + playback_pos=0; + subbuf_write_idx=1; + memset(buffer, 0, sizeof(buffer)); +} +//---- + +template +template +inline U pmf_audio_buffer::read_sample() +{ + // read sample from the buffer and clip to given number of bits + enum {sample_range=1<>1}; + uint16_t pbpos=playback_pos; + T rawsmp = buffer[pbpos] >> preshift; + U smp=U(rawsmp+halfrange); + if(smp>sample_range-1) + smp=smp>((U(-1)>>1)+halfrange)?0:max_sample_val; + if(++pbpos==buffer_size) + pbpos=0; + playback_pos=pbpos; + return smp; +} +//---- + +template +pmf_mixer_buffer pmf_audio_buffer::get_mixer_buffer() +{ + // return buffer for mixing if available (i.e. not playing the one for writing) + uint16_t pbpos=playback_pos; // note: atomic read thus no need to disable interrupts + pmf_mixer_buffer buf={0, 0}; + if(subbuf_write_idx^(pbpos(); + + + // crazy optimized asm version + // Uses only 3 regs: X (r26, r27) and 1 other reg + // ONLY works because: + // - G.mus.audiobuf.buffer is page-aligned (256 bytes) and power-of-2 size + // - That buffer size is int16_t[256] + // (2 pages of uint8, basically) + // Difference to C++ version: + // - pre-increments the buf pos so we skip the very first sample + + uint8_t s; // sample value + asm volatile( + // preserve state + "push %[s] \n\t" + "in %[s], __SREG__\n\t" +#ifdef USE_GPIO_REGS + "out %[T1], %[s] \n\t" + "out %[T2], r26 \n\t" + "out %[T3], r27 \n\t" +#else + "push %[s] \n\t" + "push r26 \n\t" + "push r27 \n\t" +#endif + + // -- main part start -- + "clr %[s] \n\t" // need one zero later + // load, inc, store offset + "lds r26, %a[pp] \n\t" // load pos into X + "inc r26 \n\t" // increment offset with wrap-around (carry is unused) + "sts %a[pp], r26 \n\t" // store back for next time + // offset buffer ptr (we know that the low-byte of buf is 0, since it's 256b-aligned) + "ldi r27, hi8(%a[buf])\n\t" // load high byte + "lsl r26 \n\t" // prepare for 16bit addressing (buf += (2 * offset)) + "adc r27, %[s] \n\t" // need the carry; s is known to be 0 + // X now points to the 16bit sample to read, load both halves + "ld %[s], X+ \n\t" // low byte goes into output + "ld r26, X \n\t" // high byte goes into X, which isn't needed anymore + // preshift sample + "asr r26 \n\t" // 2x 16bit signed shift right + "ror %[s] \n\t" + "asr r26 \n\t" + "ror %[s] \n\t" + // now s is the int8 sample + : [s] "=&d" (s) /*out*/ + : [buf] "p" (&G.mus.audiobuf.buffer[0]) /*in*/ + , [pp] "p" (&G.mus.audiobuf.playback_pos) +#ifdef USE_GPIO_REGS + , [T1] "I" (_SFR_IO_ADDR(GPIOR0)) + , [T2] "I" (_SFR_IO_ADDR(GPIOR1)) + , [T3] "I" (_SFR_IO_ADDR(GPIOR2)) +#endif + : "r26", "r27" /*clob*/ + ); + s += 127; // make unsigned + + OCR0A = s; + OCR0B = s; + + // restore state and return + asm volatile( +#ifdef USE_GPIO_REGS + "in r27, %[T3] \n\t" + "in r26, %[T2] \n\t" + "in %[s], %[T1] \n\t" +#else + "pop r27 \n\t" + "pop r26 \n\t" + "pop %[s] \n\t" +#endif + "out __SREG__, %[s]\n\t" + "pop %[s] \n\t" + "reti \n\t" + : /*out*/ + : [s] "r" (s) /*in*/ +#ifdef USE_GPIO_REGS + , [T1] "I" (_SFR_IO_ADDR(GPIOR0)) + , [T2] "I" (_SFR_IO_ADDR(GPIOR1)) + , [T3] "I" (_SFR_IO_ADDR(GPIOR2)) +#endif + ); + +} + +//---- + +uint32_t pmf_player::get_sampling_freq(uint32_t sampling_freq_) const +{ + return sampling_freq_; +} +//---- + +void pmf_player::start_playback(uint32_t sampling_freq_) +{ + DDRB |= _BV(7); + DDRG |= _BV(5); + + // PWM + TCCR0A = (1<sample_speed) + continue; + + // get channel attributes + size_t sample_addr=(size_t)(m_pmf_file+pgm_read_dword(channel->smp_metadata+pmfcfg_offset_smp_data)); + uint16_t sample_len=pgm_read_word(channel->smp_metadata+pmfcfg_offset_smp_length);/*todo: should be dword*/ + uint16_t loop_len=pgm_read_word(channel->smp_metadata+pmfcfg_offset_smp_loop_length_and_panning);/*todo: should be dword*/ + uint8_t volume=(uint16_t(channel->sample_volume)*(channel->vol_env.value>>9))>>8; + register uint8_t sample_pos_frc=channel->sample_pos; + register uint16_t sample_pos_int=sample_addr+(channel->sample_pos>>8); + register uint16_t sample_speed=channel->sample_speed; + register uint16_t sample_end=sample_addr+sample_len; + register uint16_t sample_loop_len=loop_len; + register uint8_t sample_volume=volume; + register uint8_t zero=0, upper_tmp; + + asm volatile + ( + "push %A[buffer_pos] \n\t" + "push %B[buffer_pos] \n\t" + + "mix_samples_%=: \n\t" + "lpm %[upper_tmp], %a[sample_pos_int] \n\t" + "mulsu %[upper_tmp], %[sample_volume] \n\t" + "mov %[upper_tmp], r1 \n\t" + "lsl %[upper_tmp] \n\t" + "sbc %[upper_tmp], %[upper_tmp] \n\t" + "ld __tmp_reg__, %a[buffer_pos] \n\t" + "add __tmp_reg__, r1 \n\t" + "st %a[buffer_pos]+, __tmp_reg__ \n\t" + "ld __tmp_reg__, %a[buffer_pos] \n\t" + "adc __tmp_reg__, %[upper_tmp] \n\t" + "st %a[buffer_pos]+, __tmp_reg__ \n\t" + "add %[sample_pos_frc], %A[sample_speed] \n\t" + "adc %A[sample_pos_int], %B[sample_speed] \n\t" + "adc %B[sample_pos_int], %[zero] \n\t" + "cp %A[sample_pos_int], %A[sample_end] \n\t" + "cpc %B[sample_pos_int], %B[sample_end] \n\t" + "brcc sample_end_%= \n\t" + "next_sample_%=: \n\t" + "cp %A[buffer_pos], %A[buffer_end] \n\t" + "cpc %B[buffer_pos], %B[buffer_end] \n\t" + "brne mix_samples_%= \n\t" + "rjmp done_%= \n\t" + + "sample_end_%=: \n\t" + /*todo: implement bidi loop support*/ + "sub %A[sample_pos_int], %A[sample_loop_len] \n\t" + "sbc %B[sample_pos_int], %B[sample_loop_len] \n\t" + "mov __tmp_reg__, %A[sample_loop_len] \n\t" + "or __tmp_reg__, %B[sample_loop_len] \n\t" + "brne next_sample_%= \n\t" + "clr %A[sample_speed] \n\t" + "clr %B[sample_speed] \n\t" + + "done_%=: \n\t" + "clr r1 \n\t" + "pop %B[buffer_pos] \n\t" + "pop %A[buffer_pos] \n\t" + + :[sample_speed] "+l" (sample_speed) + ,[sample_pos_frc] "+l" (sample_pos_frc) + ,[sample_pos_int] "+z" (sample_pos_int) + + :[sample_end] "r" (sample_end) + ,[sample_volume] "a" (sample_volume) + ,[upper_tmp] "a" (upper_tmp) + ,[zero] "r" (zero) + ,[sample_loop_len] "l" (sample_loop_len) + ,[buffer_pos] "e" (buffer_begin) + ,[buffer_end] "l" (buffer_end) + ); + + // store values back to the channel data + channel->sample_pos=(long(sample_pos_int-sample_addr)<<8)+sample_pos_frc; + channel->sample_speed=sample_speed; + } while(++channel!=channel_end); + + // advance buffer + ((int16_t*&)buf_.begin)+=num_samples_; + buf_.num_samples-=num_samples_; +} +//---- + +pmf_mixer_buffer pmf_player::get_mixer_buffer() +{ + return G.mus.audiobuf.get_mixer_buffer(); +} +//--------------------------------------------------------------------------- + +//=========================================================================== +#endif // ARDUINO_ARCH_AVR + +#endif diff --git a/democode/src/demolib/pmf_player_def.h b/democode/src/demolib/pmf_player_def.h new file mode 100644 index 0000000..68cc56a --- /dev/null +++ b/democode/src/demolib/pmf_player_def.h @@ -0,0 +1,306 @@ +//============================================================================ +// PMF Player +// +// Copyright (c) 2019, Profoundic Technologies, Inc. +// All rights reserved. +//---------------------------------------------------------------------------- +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Profoundic Technologies nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL PROFOUNDIC TECHNOLOGIES BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//============================================================================ + +#ifndef PFC_PMF_PLAYER_DEF_H +#define PFC_PMF_PLAYER_DEF_H +//--------------------------------------------------------------------------- + + +//============================================================================ +// interface +//============================================================================ +// external +#include "pmf_data.h" + +// new +struct pmf_channel_info; +struct pmf_mixer_buffer; +class pmf_player; +template struct pmf_audio_buffer; +typedef void(*pmf_row_callback_t)(void *custom_data_, uint8_t channel_idx_, uint8_t ¬e_idx_, uint8_t &inst_idx_, uint8_t &volume_, uint8_t &effect_, uint8_t &effect_data_); +typedef void(*pmf_tick_callback_t)(void *custom_data_); +//--------------------------------------------------------------------------- + + +//=========================================================================== +// PMF player config +//=========================================================================== +enum {pmfplayer_max_channels=9}; // maximum number of audio playback channels (reduce to save dynamic memory) +#define PMF_USE_STEREO_MIXING 1 // use stereo mixing if supported (interleaved in the audio output buffer) +#define PMF_USE_LINEAR_INTERPOLATION 0 // interpolate samples linearly for better sound quality (more performanmce intensive) +#define PFC_USE_SGTL5000_AUDIO_SHIELD 0 // enable playback through SGTL5000-based audio shield (Teensy) +#define PMF_USE_SERIAL_LOGS 0 // enable logging to serial output (disable to save memory) +//--------------------------------------------------------------------------- + +//=========================================================================== +// e_pmf_effect/e_pmf_subfx +//=========================================================================== +enum e_pmf_effect +{ + // global control + pmffx_set_speed_tempo, // [1, 255], [1, 32]=speed, [33, 255]=tempo + pmffx_position_jump, // [0, song_len-1] + pmffx_pattern_break, // [0, 255] + // channel effects + pmffx_volume_slide, // [00xxyyyy], x=slide type (0=slide down, 1=slide up, 2=fine slide down, 3=fine slide up), y=slide value [1, 15], if y=0, use previous slide type & value (x is ignored). + pmffx_note_slide_down, // [1, 0xdf] = normal slide, [0xe0, 0xef] = extra fine slide, [0xf0, 0xff] = fine slide, 0=use previous slide value + pmffx_note_slide_up, // [1, 0xdf] = normal slide, [0xe0, 0xef] = extra fine slide, [0xf0, 0xff] = fine slide, 0=use previous slide value + pmffx_note_slide, // [1, 0xdf] = slide, 0=use previous slide value, [0xe0, 0xff]=unused + pmffx_arpeggio, // x=[0, 15], y=[0, 15] + pmffx_vibrato, // [xxxxyyyy], x=vibrato speed, y=vibrato depth + pmffx_tremolo, // [xxxxyyyy], x=tremolo speed, y=tremolo depth + pmffx_note_vol_slide, // [000xyyyy], x=vol slide type (0=down, 1=up), y=vol slide value [1, 15], if y=0, use previous slide type & value (x is ignored). + pmffx_vibrato_vol_slide, // [000xyyyy], x=vol slide type (0=down, 1=up), y=vol slide value [1, 15], if y=0, use previous slide type & value (x is ignored). + pmffx_retrig_vol_slide, // [xxxxyyyy], x=volume slide param, y=sample retrigger frequency + pmffx_set_sample_offset, // [xxxxxxxx], offset=x*256 + pmffx_subfx, // [xxxxyyyy], x=sub-effect, y=sub-effect value + pmffx_panning, // [xyzwwwww], x=type (0=set, 1=slide), y=precision (0=normal, 1=fine), z=direction (0=left, 1=right), w=value (if x=0, pan value is [yzwwwww]) +}; +//---- + +enum e_pmf_subfx +{ + pmfsubfx_set_glissando, // 0=off, 1=on (when enabled "note slide" slides half a not at a time) + pmfsubfx_set_finetune, // [-8, 7] + pmfsubfx_set_vibrato_wave, // [0xyy], x=[0=retrigger, 1=no retrigger], yy=vibrato wave=[0=sine, 1=ramp down, 2=square, 3=random] + pmfsubfx_set_tremolo_wave, // [0xyy], x=[0=retrigger, 1=no retrigger], yy=tremolo wave=[0=sine, 1=ramp down, 2=square, 3=random] + pmfsubfx_pattern_delay, // [1, 15] + pmfsubfx_pattern_loop, // [0, 15], 0=set loop start, >0 = loop N times from loop start + pmfsubfx_note_cut, // [0, 15], cut on X tick + pmfsubfx_note_delay, // [0, 15], delay X ticks +}; +//--------------------------------------------------------------------------- + + +//=========================================================================== +// pmf_channel_info +//=========================================================================== +struct pmf_channel_info +{ + uint8_t base_note; + uint8_t volume; + uint8_t effect; + uint8_t effect_data; + uint8_t note_hit; +}; +//--------------------------------------------------------------------------- + + +//=========================================================================== +// pmf_mixer_buffer +//=========================================================================== +struct pmf_mixer_buffer +{ + void *begin; + unsigned num_samples; +}; +//--------------------------------------------------------------------------- + + +//=========================================================================== +// pmf_player +//=========================================================================== +class pmf_player +{ +public: + // construction and playback setup + pmf_player(); + ~pmf_player(); + bool load(const void *pmem_pmf_file_); + void enable_playback_channels(uint8_t num_channels_); + void set_row_callback(pmf_row_callback_t, void *custom_data_=0); + void set_tick_callback(pmf_tick_callback_t, void *custom_data_=0); + //------------------------------------------------------------------------- + + // PMF accessors + uint8_t num_pattern_channels() const; + uint8_t num_playback_channels() const; + uint16_t playlist_length() const; + //------------------------------------------------------------------------- + + // player control + void start(uint32_t sampling_freq_=22050, uint16_t playlist_pos_=0); + void stop(); + void update(); + //------------------------------------------------------------------------- + + // playback state accessors + bool is_playing() const; + uint8_t playlist_pos() const; + uint8_t pattern_row() const; + uint8_t pattern_speed() const; + pmf_channel_info channel_info(uint8_t channel_idx_) const; + //------------------------------------------------------------------------- + +private: + struct envelope_state; + struct audio_channel; + // platform specific functions (implemented in platform specific files) + uint32_t get_sampling_freq(uint32_t sampling_freq_) const; + void start_playback(uint32_t sampling_freq_); + void stop_playback(); + void mix_buffer(pmf_mixer_buffer&, unsigned num_samples_); + pmf_mixer_buffer get_mixer_buffer(); + // platform agnostic reference functions + template void mix_buffer_impl(pmf_mixer_buffer&, unsigned num_samples_); + // audio effects + void apply_channel_effect_volume_slide(audio_channel&); + void apply_channel_effect_note_slide(audio_channel&); + void apply_channel_effect_vibrato(audio_channel&); + void apply_channel_effects(); + bool init_effect_volume_slide(audio_channel&, uint8_t effect_data_); + bool init_effect_note_slide(audio_channel&, uint8_t slide_speed_, uint16_t target_note_pediod_); + void init_effect_vibrato(audio_channel&, uint8_t vibrato_depth_, uint8_t vibrato_speed_); + void evaluate_envelope(envelope_state&, uint16_t env_data_offs_, bool is_note_off_); + void evaluate_envelopes(); + // pattern playback + uint16_t get_note_period(uint8_t note_idx_, int16_t finetune_); + int16_t get_sample_speed(uint16_t note_period_, bool forward_); + void set_instrument(audio_channel&, uint8_t inst_idx_, uint8_t note_idx_); + void hit_note(audio_channel&, uint8_t note_idx_, uint8_t sample_start_pos_, bool reset_sample_pos_); + void process_pattern_row(); + void process_track_row(audio_channel&, uint8_t ¬e_idx_, uint8_t &inst_idx_, uint8_t &volume_, uint8_t &effect_, uint8_t &effect_data_); + void init_pattern(uint8_t playlist_pos_, uint8_t row_=0); + //------------------------------------------------------------------------- + + //========================================================================= + // envelope_state + //========================================================================= + struct envelope_state + { + uint16_t tick; + int8_t pos; + uint16_t value; + }; + //------------------------------------------------------------------------- + + //========================================================================= + // audio_channel + //========================================================================= + struct audio_channel + { + // track state + const uint8_t *track_pos; + const uint8_t *track_loop_pos; + uint8_t track_bit_pos; + uint8_t track_loop_bit_pos; + uint8_t decomp_type; + uint8_t decomp_buf[6][2]; + uint8_t track_loop_decomp_buf[6][2]; + // visualization + uint8_t note_hit; // note hit + // sample playback + const uint8_t *inst_metadata; + const uint8_t *smp_metadata; + uint32_t sample_pos; // sample position (24.8 fp) + int16_t sample_speed; // sample speed (8.8 fp) + int16_t sample_finetune; // sample finetune (9.7 fp) + uint16_t note_period; // current note period + uint8_t sample_volume; // sample volume (0.8 fp) + int8_t sample_panning; // sample panning (-127=left, 0=center, 127=right, -128=surround) + uint8_t base_note_idx; // base note index + int8_t inst_note_idx_offs; // instrument note offset + // sound effects + uint8_t effect; // current effect + uint8_t effect_data; // current effect data + uint8_t vol_effect; // current volume effect + int8_t fxmem_panning_spd; // panning + uint8_t fxmem_arpeggio; // arpeggio + uint8_t fxmem_note_slide_spd; // note slide speed + uint16_t fxmem_note_slide_prd; // note slide target period + uint8_t fxmem_vol_slide_spd; // volume slide speed & type + uint8_t fxmem_vibrato_spd; // vibrato speed + uint8_t fxmem_vibrato_depth; // vibrato depth + uint8_t fxmem_vibrato_wave; // vibrato waveform index & retrigger bit + int8_t fxmem_vibrato_pos; // vibrato wave pos + uint8_t fxmem_retrig_count; // sample retrigger count + uint8_t fxmem_note_delay_idx; // note delay note index + uint16_t vol_fadeout; // fadeout volume + envelope_state vol_env; // volume envelope + envelope_state pitch_env; // pitch envelope + }; + //------------------------------------------------------------------------- + + // PMF info + const uint8_t *m_pmf_file; + uint32_t m_sampling_freq; + pmf_row_callback_t m_row_callback; + void *m_row_callback_custom_data; + pmf_tick_callback_t m_tick_callback; + void *m_tick_callback_custom_data; + uint16_t m_pmf_flags; // e_pmf_flags + uint16_t m_note_period_min; + uint16_t m_note_period_max; + uint8_t m_note_slide_speed; + uint8_t m_num_pattern_channels; + uint8_t m_num_instruments; + uint8_t m_num_samples; + // audio channel states + uint8_t m_num_playback_channels; + uint8_t m_num_processed_pattern_channels; + audio_channel m_channels[pmfplayer_max_channels]; + // audio buffer state + uint16_t m_num_batch_samples; + uint16_t m_batch_pos; + // pattern playback state + uint16_t m_current_pattern_playlist_pos; + uint8_t m_current_pattern_last_row; + uint8_t m_current_pattern_row_idx; + uint8_t m_current_row_tick; + uint8_t m_speed; + uint8_t m_arpeggio_counter; + uint8_t m_pattern_delay; + uint8_t m_pattern_loop_cnt; + uint8_t m_pattern_loop_row_idx; +}; +//--------------------------------------------------------------------------- + + +//=========================================================================== +// pmf_audio_buffer +//=========================================================================== +template +struct pmf_audio_buffer +{ + // construction & accessors + void reset(); + template inline U read_sample(); + pmf_mixer_buffer get_mixer_buffer(); + //------------------------------------------------------------------------- + + enum {buf_size=buffer_size}; + enum {subbuf_size=buffer_size/2}; + T buffer[buffer_size]; + volatile uint16_t playback_pos; + uint8_t subbuf_write_idx; +}; +//--------------------------------------------------------------------------- + +//============================================================================ +#endif diff --git a/democode/src/demolib/raster.cpp b/democode/src/demolib/raster.cpp new file mode 100644 index 0000000..065cf6b --- /dev/null +++ b/democode/src/demolib/raster.cpp @@ -0,0 +1,249 @@ +#include "raster.h" +#include "draw.h" + +raster::TriFilterResult raster::filterTris(uint8_t *triidxs, const ivec2 * const vs, const uint8_t *clipbuf, const raster::Tri *tris, uint16_t nTris) +{ + uint8_t nFront = 0, nBack = 0; + for(uint16_t i = 0; i < nTris; ++i) + { + const raster::Tri tri = tris[i]; + + // Early clipping check + if(clipbuf) + { + uint8_t clip0 = clipbuf[tri.a]; + uint8_t clip1 = clipbuf[tri.b]; + uint8_t clip2 = clipbuf[tri.c]; + + if(!(clip0 | clip1 | clip2)) + goto accept; // All verts inside of viewport + + if(clip0 & clip1 & clip2) + continue; // All verts are outside of the viewport on the same side + + if(clip0 | clip1 | clip2) + continue; + } + +accept: + + ivec2 v0 = vs[tri.a]; + ivec2 v1 = vs[tri.b]; + ivec2 v2 = vs[tri.c]; + + if(raster::isFrontFace(v0, v1, v2)) + triidxs[nFront++] = i; + else + triidxs[nTris - ++nBack] = i; + } + + raster::TriFilterResult res; + res.nFront = nFront; + res.nBack = nBack; + return res; +} + +static FORCEINLINE vtx::Zval calcTriDepth(uint16_t triidx, const raster::Tri *tris, const vtx::Zval *zs) +{ + const raster::Tri t = tris[triidx]; + return zs[t.a] + zs[t.b] + zs[t.c]; // proper average isn't necessary +} + +struct TriCmp +{ + const vtx::Zval * const _tz; + const raster::Tri * const _tri; + TriCmp(const vtx::Zval *tz, const raster::Tri *tri) : _tz(tz), _tri(tri) {} + bool operator()(uint8_t A, uint8_t B) const + { + return calcTriDepth(A, _tri, _tz) < calcTriDepth(B, _tri, _tz); + } +}; + +struct TriCmpLUT +{ + const vtx::Zval * const _lut; + TriCmpLUT(const vtx::Zval *lut) : _lut(lut) {} + bool operator()(uint8_t A, uint8_t B) const + { + return _lut[A] < _lut[B]; + } +}; + +void raster::sortTrisZ(uint8_t *triidxs, uint8_t n, const raster::Tri *tris, uint16_t maxtris, const vtx::Zval *zv) +{ + FGLCD_ASSERT(zv, "sortidx"); + +#if CFG_FAST_TRIANGLE_SORTING+0 + vtx::Zval *lut = (vtx::Zval*)StackAlloc(maxtris * sizeof(vtx::Zval)); + for(uint8_t i = 0; i < n; ++i) + { + uint8_t triidx = triidxs[i]; + lut[triidx] = calcTriDepth(triidx, tris, zv); + } + TriCmpLUT cmp(lut); +#else + TriCmp cmp(zv, tris); +#endif + + fgstd::sort(triidxs, triidxs + n, cmp); +} + +uint8_t raster::getScreeenspaceShade(vtx::Zval za, vtx::Zval zb, vtx::Zval zc) +{ + sort3(za, zb, zc); + return uint8_t(zc - za); // known to be positive +} + +void raster::drawWireframe(const uint8_t *triidxs, uint16_t n, Color col, const raster::Tri *tris, const ivec2 *vs) +{ + if(tris) + { + for(uint16_t i = 0; i < n; ++i) + { + const raster::Tri tri = tris[triidxs[i]]; + const ivec2 v0 = vs[tri.a]; + const ivec2 v1 = vs[tri.b]; + const ivec2 v2 = vs[tri.c]; + + // Doesn't 100% prevent drawing a line twice but makes a big difference + if(tri.a < tri.b) + Draw::drawline(v0.x, v0.y, v1.x, v1.y, col); + if(tri.b < tri.c) + Draw::drawline(v1.x, v1.y, v2.x, v2.y, col); + if(tri.c < tri.a) + Draw::drawline(v2.x, v2.y, v0.x, v0.y, col); + } + } + else + { + for(uint16_t i = 0; i < n; ++i) + { + uint16_t triidx = triidxs[i] * 3; + const ivec2 v0 = vs[triidx]; + const ivec2 v1 = vs[triidx+1]; + const ivec2 v2 = vs[triidx+2]; + Draw::drawline(v0.x, v0.y, v1.x, v1.y, col); + Draw::drawline(v1.x, v1.y, v2.x, v2.y, col); + Draw::drawline(v2.x, v2.y, v0.x, v0.y, col); + } + } +} + + +void raster::CastY::init(const ivec2 v, const ivec2 m) +{ + //FGLCD_ASSERT(v.y <= m.y, "rciy"); + uint16_t dx = vabs(m.x - v.x); + uint16_t dy = m.y - v.y; // _sy == 1 + _sx = v.x < m.x ? 1 : -1; + _m = m; + looptype = dy > dx; + if(looptype) + vswap(dy, dx); + + _dx2 = dx * 2; + _dy2 = dy * 2; + _err = _dy2 - dx; + if(!_dx2) + looptype = 2; +} + +// true if goal reached, false if aborted due to y +bool raster::CastY::cast(ivec2& vIn, const int y) +{ + const int yy = vmin(y, _m.y); + ivec2 v = vIn; + switch(looptype) + { + case 0: + while(v.y != yy) + { + while(_err >= 0) + { + ++v.y; + _err -= _dx2; + } + v.x += _sx; + _err += _dy2; + } + break; + + case 1: + while(v.y != yy) + { + while(_err >= 0) + { + v.x += _sx; + _err -= _dx2; + } + ++v.y; + _err += _dy2; + } + break; + + case 2: + vIn.x = _m.x; + //FGLCD_ASSERT(v.y == _m.y, "yywat"); + return true; + } + + //FGLCD_ASSERT(v.y == y || v.y == _m.y, "ywat"); + vIn = v; + return (v.y == _m.y); +} + +raster::AABB raster::getVertexListAABB(const ivec2 *pv, const uint16_t n) +{ + //ASSERT(n); + raster::AABB ret(pv[0]); // HACK: don't n == 0; + for(uint16_t i = 1; i < n; ++i) + ret.extend(pv[i]); + return ret; +} + +/* +static AABB getTrisAABB(const ivec2 *pv, const uint8_t *pidx, const uint8_t n, LCD::ColorType col) +{ + AABB ret; + for(uint8_t i = 0; i < n; ++i, pidx += 3) // FIXME: autoinc + { + const ivec2 v0 = pv[pidx[0]], v1 = pv[pidx[1]], v2 = pv[pidx[2]]; + const ivec2 pmin(vmin3(v0.x, v1.x, v2.x), vmin3(v0.y, v1.y, v2.y)); + const ivec2 pmax(vmax3(v0.x, v1.x, v2.x), vmax3(v0.y, v1.y, v2.y)); + ret.extendMin(pmin); + ret.extendMax(pmax); + } + ret.shrink(ivec2(0,0), ivec2(LCD::XMAX, LCD::YMAX)); + return ret; +} +*/ + +void raster::ClearHelperBase::_clearV(Color col, uint8_t k, uint8_t N) const +{ + const raster::AABB bb = bound; + uint16_t x = (bb.start.x & ~(N - 1)) + k; + const uint16_t end = uint16_t(bb.end.x); + for( ; x <= end; x += N) + LCD::fillrectfromto(x, bb.start.y, x, bb.end.y, col); +} + +void raster::ClearHelperBase::_clearH(Color col, uint8_t k, uint8_t N) const +{ + const raster::AABB bb = bound; + uint16_t y = (bb.start.y & ~(N - 1)) + k; + const uint16_t end = uint16_t(bb.end.y); + for( ; y <= end; y += N) + LCD::fillrectfromto(bb.start.x, y, bb.end.x, y, col); +} + +void raster::ClearHelperBase::_recalc(const AABB * aa, uint8_t N, uint8_t padding) +{ + AABB bb; + for(uint8_t i = 1; i < N; ++i) + bb.extend(aa[i]); + if(padding) + bb.widen(padding); + bb.clampToLCD(); + bound = bb; +} diff --git a/democode/src/demolib/raster.h b/democode/src/demolib/raster.h new file mode 100644 index 0000000..c5a6367 --- /dev/null +++ b/democode/src/demolib/raster.h @@ -0,0 +1,317 @@ +// Low-level rasterizer. For the high-level crap, see drawmesh.h + +#pragma once + +#include "cfg-demo.h" +#include "../demomath/fgmath.h" +#include "vertex.h" + +#include + +namespace raster { + +typedef LCD::ColorType Color; + +struct Tri +{ + uint8_t a, b, c; +}; + +struct Params +{ + int8_t align; // must be power of 2 + int8_t alignoffs; + int8_t incr; + //uint8_t face; // 0: only backface, 1: only frontface, else: anything + //uint8_t backfaceoffs; + //uint8_t backfaceColorOffs; + + // FIXME: remove those from here + uint8_t rubmul; + uint8_t glitchmul; +}; + +struct AABB +{ + FORCEINLINE AABB() : start(SHRT_MAX), end(SHRT_MIN) {} + FORCEINLINE AABB(const ivec2& v) : start(v), end(v) {} + ivec2 start, end; + void extend(const ivec2& p) + { + extendMin(p); + extendMax(p); + } + void extend(const AABB& p) + { + extendMin(p.start); + extendMax(p.end); + } + void widen(uint8_t w) + { + start.x -= w; + start.y -= w; + end.x += w; + end.y += w; + } + void clampToLCD() + { + //shrink(ivec2(0,0), ivec2(LCD::XMAX, LCD::YMAX)); + + start.x = vclamp(start.x, 0, LCD::XMAX); + start.y = vclamp(start.y, 0, LCD::YMAX); + end.x = vclamp(end.x, 0, LCD::XMAX); + end.y = vclamp(end.y, 0, LCD::YMAX); + } + FORCEINLINE void shrink(const ivec2& b, const ivec2& e) + { + start.x = vmax(start.x, b.x); + start.y = vmax(start.y, b.y); + end.x = vmin(end.x, e.x); + end.y = vmin(end.y, e.y); + } + FORCEINLINE void extendMin(const ivec2& p) + { + start.x = vmin(start.x, p.x); + start.y = vmin(start.y, p.y); + } + FORCEINLINE void extendMax(const ivec2& p) + { + end.x = vmax(end.x, p.x); + end.y = vmax(end.y, p.y); + } + FORCEINLINE ivec2 size() const + { + return end - start + 1; + } + FORCEINLINE bool valid() const + { + return start.x <= end.x; + } +}; + +struct ClearHelperBase +{ + void _clearH(Color col, uint8_t k, uint8_t N) const; + void _clearV(Color col, uint8_t k, uint8_t N) const; + void _recalc(const AABB *aa, uint8_t N, uint8_t padding); + AABB bound; +}; + +template // must be power of 2 +struct ClearHelperN : ClearHelperBase +{ + AABB aa[N]; + uint8_t r, w; + + inline void add(const AABB& a, uint8_t padding) + { + aa[w++ & (N - 1)] = a; + this->_recalc(&aa[0], N, padding); + } +}; + +template // N must be power of 2 +struct ClearHelper : ClearHelperN +{ + inline void clear(Color col) + { + uint8_t k = (this->r + 1) & (N - 1); + this->r = k; + this->_clearV(col, k*stride+offset, N); + } +}; + +template // N must be power of 2 +struct ClearHelperH : ClearHelperN +{ + inline void clear(Color col) + { + uint8_t k = (this->r + 1) & (N - 1); + this->r = k; + this->_clearH(col, k*stride+offset, N); + } +}; + +struct CastY +{ + void init(const ivec2 v, const ivec2 m); + bool cast(ivec2& vIn, const int y); +private: + uint8_t looptype; + uint16_t _dx2, _dy2; + int16_t _err; + int8_t _sx; + ivec2 _m; +}; + +static FORCEINLINE uint8_t isFrontFace(const ivec2 v0, const ivec2 v1, const ivec2 v2) +{ + return (v1.y - v0.y) * (v2.x - v1.x) - (v1.x - v0.x) * (v2.y - v1.y) < 0; +} + +static FORCEINLINE void sortverts(ivec2& top, ivec2& midL, ivec2& btmR) +{ + if(top.y > btmR.y) + vswap(top, btmR); + if(top.y > midL.y) + vswap(top, midL); + if(midL.y > btmR.y) + vswap(midL, btmR); +} + +AABB getVertexListAABB(const ivec2 *pv, const uint16_t n); + + +// low-level draw function. +// vertices must be passed properly sorted. y >= 0. +template +static FORCEINLINE void drawTriLow(Render& render, const ivec2 top, const ivec2 midL, const ivec2 btmR, int y, const uint8_t incr, const uint8_t colid) +{ + FGLCD_ASSERT(top.y <= midL.y && midL.y <= btmR.y, "drawtrio"); + + const int h = vmin(btmR.y, render.height()); + + if(y >= h) + return; + + // all points in order now + ivec2 pL = top, pR = top; + CastY c1, c2; + c1.init(pL, midL); + c2.init(pR, btmR); + const typename Render::ColorType col = render.getColor(colid); + uint8_t once = 0; + do + { + if(c1.cast(pL, y)) + { + if(once) + return; + once = 1; + pL = midL; // this avoids disconnected edges + c1.init(pL, btmR); + } + c2.cast(pR, y); + + if(pL.x != pR.x) + { + int x1 = pL.x; + int x2 = pR.x; + makeminmax(x1, x2); + render.drawScanline(x1, x2, y, col); + } + + y += incr; + } + while(y < h); +} + +// the larger this number, the more to dampen the color +uint8_t getScreeenspaceShade(vtx::Zval za, vtx::Zval zb, vtx::Zval zc); + +struct TriFilterResult +{ + uint16_t nFront; + uint16_t nBack; +}; + +// Filters indices of visible tris into triidxs: +// - At the front of the list when it's a front-facing triangle +// - At the back of the list when it's a back-facing triangle +TriFilterResult filterTris(uint8_t *triidxs, const ivec2 * const vs, const uint8_t *clipbuf, const Tri *tris, uint16_t nTris); + +// Pass a filtered list of indices to sort. +// After sorting the back-most triangles will be first in the list. +// (Use this after filtering to save sorting time) +void sortTrisZ(uint8_t *triidxs, uint8_t n, const Tri *tris, uint16_t maxtris, const vtx::Zval * const zv); + +template +void sortTris(uint8_t *triidxs, uint8_t n, const Tri *tris, const LESS& cmp) +{ + struct Helper + { + uint8_t * const triidxs; + const Tri * const tris; + const LESS& cmp; + + FORCEINLINE Helper(uint8_t *triidxs, const Tri *tris, const LESS& cmp) + : triidxs(triidxs), tris(tris), cmp(cmp) + {} + + FORCEINLINE bool operator() (uint8_t a, uint8_t b) const + { + return cmp(tris[triidxs[a]], tris[triidxs[b]]); + } + }; + + Helper h(triidxs, tris, cmp); + fgstd::sort(triidxs, triidxs + n, h); +} + +void drawWireframe(const uint8_t *triidxs, uint16_t n, Color col, const raster::Tri *tris, const ivec2 *vs); + +// pass tris=NULL for non-indexed rendering +// pass zbuf=NULL for unshaded rendering +template +void drawTriangles(Render& render, const uint8_t *triidxs, uint16_t n, const ivec2 *vs, const Tri *tris, const uint8_t * const colorIdxs, const vtx::Zval *zbuf, const Params& rp, uint8_t yoffs, uint8_t shadelevels, uint8_t upshade, uint8_t downshade, uint8_t coloffs) +{ + if(!zbuf) + shadelevels = 0; + + for(uint16_t i = 0; i < n; ++i) + { + uint8_t triidx = triidxs[i]; + ivec2 v0(noinit), v1(noinit), v2(noinit); + if(tris) // indexed rendering // this is a constant jump but it's here to save space (compared to specialization). on AVR branches are cheap so whatev. + { + const Tri& tri = tris[triidx]; + v0 = vs[tri.a]; + v1 = vs[tri.b]; + v2 = vs[tri.c]; + } + else // triangle soup without index + { + v0 = *vs++; + v1 = *vs++; + v2 = *vs++; + } + + sortverts(v0, v1, v2); + + // Convert mesh color index to palette index + uint8_t colid; + if(shadelevels) + { + const uint8_t firstcolor = shadelevels + coloffs; + const uint8_t colordist = shadelevels << 1; + const uint8_t palbase = firstcolor + colordist * colorIdxs[triidx]; + + vtx::Zval za, zb, zc; + if(tris) + { + const Tri& tri = tris[triidx]; + za = zbuf[tri.a]; + zb = zbuf[tri.b]; + zc = zbuf[tri.c]; + } + else + { + za = *zbuf++; + zb = *zbuf++; + zc = *zbuf++; + } + uint8_t shade = raster::getScreeenspaceShade(za, zb, zc); + + colid = palbase + scale8(shadelevels, upshade) - scale8(shade, downshade); + } + else + colid = colorIdxs[triidx]; + + int y = ((vmax(v0.y, 0)-1) | (rp.align - 1)) + ((rp.incr) + rp.alignoffs); + y += yoffs; + + drawTriLow(render, v0, v1, v2, y, rp.incr, colid); + } +} + +} // end namespace raster diff --git a/democode/src/demolib/render.h b/democode/src/demolib/render.h new file mode 100644 index 0000000..64bcc5c --- /dev/null +++ b/democode/src/demolib/render.h @@ -0,0 +1,70 @@ +// Rendering helpers used to specialize rasterizer + +#pragma once + +#include "cfg-demo.h" +#include "../demomath/fgmath.h" +#include +#include "fastlookup.h" // for ISIN8FAST +#include "drawhelper.h" +#include "raster.h" + + +namespace render { + + +static FORCEINLINE void drawScanlineLCD(int x0, int x1, unsigned y, LCD::ColorType col) +{ + x0 = vclamp(x0, 0, LCD::XMAX); + x1 = vclamp(x1, 0, LCD::XMAX); + + LCD::setxy(x0, y, x1, y); + LCD::sendPixel(col); // x0, x1 are inclusive so we'all always send at least one pixel + LCD::fastfill_u16(x1 - x0); // fill the remaining scanline with the color just set +} + +struct ToLCD +{ + ToLCD(uint8_t rub, uint8_t glitch) + : rubmul(rub), glitchmul(glitch) + {} + typedef LCD::ColorType ColorType; + FORCEINLINE static ColorType getColor(uint8_t id) { return palgetcolor_noinl(id); }; + FORCEINLINE void drawScanline(int x0, int x1, int y, ColorType col) const + { + // yeah yeah i know this is a cheap, lame fake and no real rubber. deal with it. + if(rubmul) + { + int8_t rub = int8_t(y); + if(glitchmul) + incrhiscale(rub, uint8_t(uint8_t(x0) ^ uint8_t(x1)), glitchmul); + rub = hiscale8(ISIN8FAST(rub), (int8_t)rubmul); + x0 += rub; + x1 += rub; + } + drawScanlineLCD(x0, x1, y, col); + } + FORCEINLINE static int height() { return LCD::HEIGHT; } + const uint8_t rubmul, glitchmul; +}; + +struct ToTex +{ + ToTex(uint8_t *tex, uint8_t w, uint8_t h) + : _tex(tex), _w(w), _h(h) + {} + FORCEINLINE int height() const { return _h; } + typedef uint8_t ColorType; // pass-through color ID + FORCEINLINE static uint8_t getColor(uint8_t id) { return id; }; + FORCEINLINE void drawScanline(int x0, int x1, int y, ColorType col) + { + const uint8_t xx0 = (uint8_t)vclamp(x0, 0, _w); + const uint8_t xx1 = (uint8_t)vclamp(x1, 0, _w); + fglcd::RAM::Memset(&_tex[uint16_t(y) * _w + xx0], col, xx1-xx0+1); + } + + uint8_t * const _tex; + const uint8_t _w, _h; +}; + +} // end namespace render diff --git a/democode/src/demolib/scratch.h b/democode/src/demolib/scratch.h new file mode 100644 index 0000000..483187a --- /dev/null +++ b/democode/src/demolib/scratch.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include "cfg-demo.h" +#include "globals.h" + +// msvc you piece of fuck, why +#ifdef _MSC_VER + +#if CFG_SCRATCH_MAX_AREAS+0 +#define scratch0 (&G.scratchblock[0]) +#endif +#if CFG_SCRATCH_MAX_AREAS > 1 +#define scratch1 (&G.scratchblock[CFG_SCRATCH_BLOCK_SIZE]) +#endif +#if CFG_SCRATCH_MAX_AREAS > 2 +#define scratch2 (&G.scratchblock[2*CFG_SCRATCH_BLOCK_SIZE]) +#endif +#if CFG_SCRATCH_MAX_AREAS > 3 +#define scratch3 (&G.scratchblock[3*CFG_SCRATCH_BLOCK_SIZE]) +#endif + +#else + +#if CFG_SCRATCH_MAX_AREAS+0 +static constexpr uint8_t * const scratch0 = &G.scratchblock[0]; +#if CFG_SCRATCH_MAX_AREAS > 1 +static constexpr uint8_t * const scratch1 = &G.scratchblock[CFG_SCRATCH_BLOCK_SIZE]; +#endif +#if CFG_SCRATCH_MAX_AREAS > 2 +static constexpr uint8_t * const scratch2 = &G.scratchblock[2*CFG_SCRATCH_BLOCK_SIZE]; +#endif +#if CFG_SCRATCH_MAX_AREAS > 3 +static constexpr uint8_t * const scratch3 = &G.scratchblock[3*CFG_SCRATCH_BLOCK_SIZE]; +#endif +#endif + +#endif diff --git a/democode/src/demolib/scrollhelper.h b/democode/src/demolib/scrollhelper.h new file mode 100644 index 0000000..336c306 --- /dev/null +++ b/democode/src/demolib/scrollhelper.h @@ -0,0 +1,80 @@ +#pragma once + +#include "cfg-demo.h" +#include "../demomath/fgmath.h" + +template +struct WidescrollState +{ + static constexpr uint16_t SCROLL_LCM = LCM::value; + static_assert(SCROLL_LCM > 0 && SCROLL_LCM < 0x7fff, "fuck"); + + static constexpr uint16_t SCROLL_MOD = LCD::WIDTH < SCROLL_W ? LCD::WIDTH : SCROLL_W; + +private: + static const uint8_t REDRAW_MASK = REDRAW_SIZE-1; + volatile uint16_t redraw[REDRAW_SIZE]; +public: + FORCEINLINE uint16_t getDrawCol(uint8_t idx) const { return redraw[idx & REDRAW_MASK]; } + volatile uint8_t wpos; + int8_t direction; + uint16_t scrollpos; + + WidescrollState() + : wpos(0), scrollpos(0) + { + } + + void _adjustflip() + { + const uint16_t sp = scrollpos - uint16_t(LCD::WIDTH) * oldsgn; + scrollpos = vmodpos(sp); + } + + void flip() // manual trigger + { + _adjustflip(); + direction = -direction; + oldsgn = -oldsgn; + } + + int8_t oldsgn; + void checkflip(int8_t sgn) + { + if(sgn) + { + if(!(sgn + oldsgn)) + _adjustflip(); + oldsgn = sgn; + } + } + + uint16_t scroll(int8_t adv) + { + uint8_t a = vabs(adv); + const int8_t sgn = vsgn(adv); + checkflip(sgn); + uint8_t w = wpos; + //wpos = w + a; + const uint16_t oldsp = scrollpos; + uint16_t sp = oldsp; + while(a--) + { + redraw[w++ & REDRAW_MASK] = sp; + sp += sgn; // not bad if this overflows outside of SCROLL_LCM; the actual draw code does its own modulo + } + wpos = w; + sp = vmodpos(sp); // prevent wraparound glitching and ensure the subtraction loops in vmodpos() don't take too long + scrollpos = sp; + const uint16_t spmod = vmodpos(sp); + return (uint16_t)spmod; // always positive + } + + uint16_t smooth; + uint16_t smoothscroll(uint16_t aa) + { + uint16_t s = smooth + aa; + smooth = s & 0xff; + return scroll(s >> 8); + } +}; diff --git a/democode/src/demolib/sort.h b/democode/src/demolib/sort.h new file mode 100644 index 0000000..5b7f9a0 --- /dev/null +++ b/democode/src/demolib/sort.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include "../demomath/traits.h" +#include "_algo_sort.h" + +namespace fgstd { + + + +template +void FGSTD_FORCE_INLINE sort(const ITER& begin, const ITER& end, const LESS& lt, const SWAPPER& swp) +{ + priv::quicksort(begin, end - 1, lt, swp); +} + +template +void FGSTD_FORCE_INLINE sort(const ITER& begin, const ITER& end, const LESS& lt) +{ + sort(begin, end, lt, swapper<>()); +} + +template +void FGSTD_FORCE_INLINE sort(const ITER& begin, const ITER& end) +{ + sort(begin, end, less<>()); +} + + + + +} // end namespace fgstd + diff --git a/democode/src/demolib/vertex.h b/democode/src/demolib/vertex.h new file mode 100644 index 0000000..65fe037 --- /dev/null +++ b/democode/src/demolib/vertex.h @@ -0,0 +1,101 @@ +#pragma once + +#include "../demomath/mat4.h" +#include "sort.h" +#include +#include "../demomath/fgmath.h" + + +namespace vtx { + +#if CFG_FLOAT_DEPTH+0 +typedef float Zval; +static FORCEINLINE Zval calcDepth(const vec4& v) { return Zval(v.z.tofloat() / v.w.tofloat()) } +#else +typedef int16_t Zval; +static FORCEINLINE Zval calcDepth(const vec4& v) { return v.z.int16div(v.w); } +#endif + +enum Constants +{ + COORD_BITS = 8 * sizeof(ivec2::value_type), + SCREENSPACE_BITS = COORD_BITS - 2, // this also needs to be chosen so that reasonable values of v.w don't overflow the multiplication + SCREENSPACE_MASK = (1 << SCREENSPACE_BITS) - 1, + MAXLCDSIZE = LCD::WIDTH > LCD::HEIGHT ? LCD::WIDTH : LCD::HEIGHT, + GUARDBAND_MULT = SCREENSPACE_MASK / MAXLCDSIZE, + GUARDBAND_SIZE = MAXLCDSIZE * GUARDBAND_MULT, + + // Cohen-Sutherland outcodes + OUT_U = 1, + OUT_D = 2, + OUT_L = 4, + OUT_R = 8, +}; + +static FORCEINLINE ivec2 project1(const vec4& v) +{ + return ivec2(v.x.int16div(v.w), v.y.int16div(v.w)); +} + + +static inline uint8_t calcOutcode(const vec4& v) +{ + uint8_t ret = 0; + if(v.w > 0) // inside frustum? + { + // round up int part; it's known to be positive and this avoids the case R=L=0 caused by v.w < 1.0 + const int32_t R = (v.w.intpart() + 1) * int32_t(GUARDBAND_SIZE); + const int32_t L = -R; + const int16_t xi = v.x.intpart(); + const int16_t yi = v.y.intpart(); + if(xi < L) + ret |= OUT_L; + if(yi < L) + ret |= OUT_U; + if(R < xi) + ret |= OUT_R; + if(R < yi) + ret |= OUT_D; + } + else + --ret; + + return ret; +} + +template +static FORCEINLINE vec4 transform1(const vec3& v3, const M& m) +{ + vec4 v = m * vec4(v3.x, v3.y, v3.z, fp1616(1)); + if(v.w.f.i == 0) + v.w.f.i = 1; + return v; +} + +// vec3 variant (auto-transform to vec4(v3, 1.0f) for homogenous coords) +template +static void transform_and_project(ivec2 * dst, uint8_t *clipstuff, Zval *zbuf, const svec3 *src, const M& m, const unsigned n, const ivec2& viewport) +{ + for(unsigned i = 0; i < n; ++i) + { + //assert((m.template get<3, 3>()) != M::Zero()); + //assert(v.w != M::Zero()); + + const svec3 v3 = src[i]; + vec4 vv(v3.x, v3.y, v3.z, fp1616(1)); + vec4 v = m * vv; + + // clipping + if(clipstuff) + clipstuff[i] = calcOutcode(v); + else if(v.w.f.i == 0) // will clip when w == 0, but otherwise have to check + v.w.f.i = 1; + + if(zbuf) + zbuf[i] = calcDepth(v); + + dst[i] = project1(v) + viewport; + } +} + +}; diff --git a/democode/src/demomath/_impl_avr.cpp b/democode/src/demomath/_impl_avr.cpp new file mode 100644 index 0000000..e8a7626 --- /dev/null +++ b/democode/src/demomath/_impl_avr.cpp @@ -0,0 +1,196 @@ +#ifdef __AVR__ + +#include "_impl_avr.h" + +// let the compiler figure out the signed cases + +int8_t scale8_AVR(int8_t i, uint8_t scale) +{ + return scale8_CPP(i, scale); +} + +int16_t scale16by8_AVR(int16_t i, uint8_t scale) +{ + return scale16by8_CPP(i, scale); +} + + +uint16_t scale16_AVR(uint16_t i, uint16_t scale) +{ + const uint8_t zero = 0; + uint32_t result; + asm volatile( + "mul %A[i], %A[scale] \n\t" + "movw %A[result], r0 \n\t" + "mul %B[i], %B[scale] \n\t" + "movw %C[result], r0 \n\t" + "mul %B[i], %A[scale] \n\t" + "add %B[result], r0 \n\t" + "adc %C[result], r1 \n\t" + "adc %D[result], %[zero] \n\t" + "mul %A[i], %B[scale] \n\t" + "add %B[result], r0 \n\t" + "adc %C[result], r1 \n\t" + "adc %D[result], %[zero] \n\t" + "clr r1 \n\t" + "add %A[result], %A[i] \n\t" + "adc %B[result], %B[i] \n\t" + "adc %C[result], %[zero] \n\t" + "adc %D[result], %[zero] \n\t" + : [result] "=&r" (result) + : [i] "r" (i), [scale] "r" (scale), [zero] "r" (zero) + ); + return result >> 16; +} + +/* Multiplication table: +32bit x 32bit is a 64 bit result, so we need 8 regs to store the result, in theory +Following the CPP implementation, we only want regs 5432 +(last 2 are only for temporaries, first 2 are not needed) + +DCBA x WZYX +The algorithm works via many 8bit multiplcations and summing those up. +// + indicates a carry must be added to the result (add carry to the reg where the + is) +// we can skip adding the carry where we know things won't overflow, +// and since we don't need the two upper regs marked with # at all, we can skip all operations on them) +registers: +76543210 +## ax (this means: multiply bytes A and X from the inputs, add each half to regs 0 and 1, and the carry to reg 2) +## ay +## az +## aw +##+++bx (from here, make sure to add the carry) +##++by +##+bz +##bw +##++cx +##+cy +##cz +#cw (only need the lower byte result of the multiplcation) +##+dx +##dy +#dz (only need the lower byte result of the multiplcation) +dw (don't need this at all) +*/ + +// via http://www.vfx.hu/avr/download/mult32.asm and modified +FORCEINLINE static uint32_t fp1616_umul32x32(uint32_t x, uint32_t y) +{ + uint32_t ret; + const uint8_t zero = 0; + uint16_t tmp; + asm volatile( + "clr r0 \n\t" + "movw %A[ret], r0 \n\t" + "movw %C[ret], r0 \n\t" + + "mul %A[x],%A[y] \n\t" + "movw %A[tmp],R0 \n\t" + + "A_%=: \n\t" + "tst %A[y] \n\t" + "breq B_%= \n\t" + "mul %B[x],%A[y] \n\t" + "add %B[tmp],R0 \n\t" + "adc %A[ret],R1 \n\t" + "mul %C[x],%A[y] \n\t" + "add %A[ret],R0 \n\t" + "adc %B[ret],R1 \n\t" + "mul %D[x],%A[y] \n\t" + "add %B[ret],R0 \n\t" + "adc %C[ret],R1 \n\t" + + "B_%=: \n\t" + "tst %B[y] \n\t" + "breq C_%= \n\t" + "mul %A[x],%B[y] \n\t" + "add %B[tmp],R0 \n\t" + "adc %A[ret],R1 \n\t" + "adc %B[ret],%[zero] \n\t" + "adc %C[ret],%[zero] \n\t" + "adc %D[ret],%[zero] \n\t" + "mul %B[x],%B[y] \n\t" + "add %A[ret],R0 \n\t" + "adc %B[ret],R1 \n\t" + "adc %C[ret],%[zero] \n\t" + "adc %D[ret],%[zero] \n\t" + "mul %C[x],%B[y] \n\t" + "add %B[ret],R0 \n\t" + "adc %C[ret],R1 \n\t" + "adc %D[ret],%[zero] \n\t" + "mul %D[x],%B[y] \n\t" + "add %C[ret],R0 \n\t" + "adc %D[ret],R1 \n\t" + + "C_%=: \n\t" + "tst %C[y] \n\t" + "breq D_%= \n\t" + "mul %A[x],%C[y] \n\t" + "add %A[ret],R0 \n\t" + "adc %B[ret],R1 \n\t" + "adc %C[ret],%[zero] \n\t" + "adc %D[ret],%[zero] \n\t" + "mul %B[x],%C[y] \n\t" + "add %B[ret],R0 \n\t" + "adc %C[ret],R1 \n\t" + "adc %D[ret],%[zero] \n\t" + "mul %C[x],%C[y] \n\t" + "add %C[ret],R0 \n\t" + "adc %D[ret],R1 \n\t" + "mul %D[x],%C[y] \n\t" + "add %D[ret],R0 \n\t" + "adc %G[ret],R1 \n\t" + + "D_%=: \n\t" + "tst %D[y] \n\t" + "breq E_%= \n\t" + "mul %A[x],%D[y] \n\t" + "add %B[ret],R0 \n\t" + "adc %C[ret],R1 \n\t" + "adc %D[ret],%[zero] \n\t" + "mul %B[x],%D[y] \n\t" + "add %C[ret],R0 \n\t" + "adc %D[ret],R1 \n\t" + "mul %C[x],%D[y] \n\t" + "add %D[ret],R0 \n\t" + + "E_%=: \n\t" + "clr r1 \n\t" + + : [ret] "=&r" (ret), [tmp] "=&r" (tmp) + : [x] "r" (x), [y] "r" (y), [zero] "r" (zero) + ); + return ret; +} + +#ifdef __GNUC__ +#pragma GCC optimize ("Os") +#endif +int32_t fp1616_mul_AVR(int32_t x, int32_t y) +{ + uint8_t neg = 0; + if(x < 0) + { + x = -x; + ++neg; + } + if(y < 0) + { + y = -y; + ++neg; + } + + int32_t ret = fp1616_umul32x32(x, y); + + if(neg == 1) + ret = -ret; + + return ret; +} + +int32_t fp1616_div_AVR(int32_t a, int32_t b) +{ + return fp1616_div_CPP(a, b); // TODO: this is very slow and should be replaced +} + +#endif diff --git a/democode/src/demomath/_impl_avr.h b/democode/src/demomath/_impl_avr.h new file mode 100644 index 0000000..53fb79d --- /dev/null +++ b/democode/src/demomath/_impl_avr.h @@ -0,0 +1,177 @@ +#pragma once + +#include "../demolib/demo_def.h" +#include "_impl_cpp.h" + +FORCEINLINE static uint8_t avgfast_AVR(uint8_t a, uint8_t b) +{ + asm volatile( + "add %[a], %[b] \n\t" + "ror %[a]" + : [a] "+r" (a) + : [b] "r" (b) + ); + return a; +} + +FORCEINLINE static uint8_t avgfast4_AVR(uint8_t a, uint8_t b, uint8_t c, uint8_t d) +{ + asm volatile( + "add %[a], %[b] \n\t" + "ror %[a] \n\t" + "add %[c], %[d] \n\t" + "ror %[c] \n\t" + "add %[a], %[c] \n\t" + "ror %[a]" + : [a] "+r" (a), [c] "+r" (c) + : [b] "r" (b), [d] "r" (d) + ); + return a; +} + +FORCEINLINE static uint8_t scale8_AVR(uint8_t i, uint8_t scale) +{ + asm volatile( + "mul %[a], %[s] \n\t" + "add r0, %[a] \n\t" // throw value away but keep the carry + "ldi %[a], 0x00 \n\t" // does NOT touch any flags (unlike clr) + "adc %[a], r1 \n\t" + "clr r1 \n\t" + : [a] "+d" (i) /* writes to i */ + : [s] "r" (scale) /* uses scale */ + : "r0", "r1" /* clobbers r0, r1 */ + ); + return i; +} + +FORCEINLINE static uint16_t scale16by8_AVR(uint16_t i, uint8_t scale) +{ + if(!scale) + return i; + uint16_t result = 0; + asm volatile( + "mul %A[i], %[scale] \n\t" + "add r0, %A[i] \n\t" + "adc %A[result], r1 \n\t" + "mul %B[i], %[scale] \n\t" + "add %A[result], r0 \n\t" + "adc %B[result], r1 \n\t" + "clr r1 \n\t" + "add %A[result], %B[i] \n\t" + "adc %B[result], r1 \n\t" + : [result] "+r" (result) + : [i] "r" (i), [scale] "r" (scale) + : "r0", "r1" + ); + return result; +} + +// signed is a bit trickier, let the compiler figure it out and don't inline it +int8_t scale8_AVR(int8_t i, uint8_t scale); +int16_t scale16by8_AVR(int16_t i, uint8_t scale); + +// a bit too large to inline this one +uint16_t scale16_AVR(uint16_t i, uint16_t scale); + + +FORCEINLINE uint8_t saturateAdd_AVR(uint8_t a, uint8_t b) +{ + asm volatile( + "add %[a], %[b] \n\t" + "brcc done_%= \n\t" + "ldi %[a], 0xff \n\t" + "done_%=: \n\t" + : [a] "+d" (a) /* outputs */ + : [b] "r" (b) /* inputs */ + ); + return a; +} + +FORCEINLINE uint8_t saturateSub_AVR(uint8_t a, uint8_t b) +{ + asm volatile( + "sub %[a], %[b] \n\t" + "brcc done_%= \n\t" + "clr %[a] \n\t" + "done_%=: \n\t" + : [a] "+r" (a) /* outputs */ + : [b] "r" (b) /* inputs */ + ); + return a; +} + +// avg-gcc generates a redundant MOVW when MUL is used. +// Unsigned cases are easy (there are overloads for the nasty signed variants but those are not asm) +static FORCEINLINE void incrhiscale_AVR(uint8_t& a, uint8_t b, uint8_t c) +{ + asm volatile( + "mul %[b],%[c] \n\t" + "add %[a], r1 \n\t" + "clr r1" + : [a] "+r" (a) /*outputs*/ + : [b] "r" (b), [c] "r" (c) + ); +} + +static FORCEINLINE void incrhiscale_AVR(uint16_t& a, uint8_t b, uint8_t c) +{ + asm volatile( + "mul %[b],%[c] \n\t" + "add %A[a], r1 \n\t" + "clr r1 \n\t" // does not affect carry + "adc %B[a], r1 \n\t" + : [a] "+r" (a) /*outputs*/ + : [b] "r" (b), [c] "r" (c) + ); +} + +static inline uint8_t hiscale8_AVR(uint8_t b, uint8_t c) +{ + asm volatile( + "mul %[b],%[c] \n\t" + "mov %[b], r1 \n\t" + "clr r1" + : [b] "+r" (b) + : [c] "r" (c) + ); + return b; +} + +FORCEINLINE static uint8_t avgfast16_AVR(uint8_t a, uint8_t b) +{ + asm volatile( + "add %A[a], %A[b] \n\t" + "adc %B[a], %B[b] \n\t" + "ror %B[a] \n\t" + "ror %A[a] \n\t" + : [a] "+r" (a) + : [b] "r" (b) + ); + return a; +} + + +int32_t fp1616_mul_AVR(int32_t a, int32_t b); +int32_t fp1616_div_AVR(int32_t a, int32_t b); + +// this isn't so bad and probably the most efficient way to do this +FORCEINLINE int16_t fp1616_intdiv_AVR(int32_t a, int32_t b) +{ + return fp1616_intdiv_CPP(a, b); +} + + +// No asm impl for these for now. +// Those operations shouldn't happen anyway, as the precision is way too low. +FORCEINLINE int16_t fp88_mul_AVR(int16_t a, int16_t b) +{ + return fp88_mul_CPP(a, b); +} +FORCEINLINE int16_t fp88_div_AVR(int16_t a, int16_t b) +{ + return fp88_div_CPP(a, b); +} +FORCEINLINE int8_t fp88_intdiv_AVR(int16_t a, int16_t b) +{ + return fp88_intdiv_CPP(a, b); +} diff --git a/democode/src/demomath/_impl_cpp.cpp b/democode/src/demomath/_impl_cpp.cpp new file mode 100644 index 0000000..9ac6d47 --- /dev/null +++ b/democode/src/demomath/_impl_cpp.cpp @@ -0,0 +1,91 @@ +#include +#include "../demolib/demo_def.h" +#include "_impl_cpp.h" + + +FORCEINLINE void _adc(uint8_t& carry, uint8_t& dst, uint8_t x) +{ + uint16_t res = uint16_t(dst) + uint16_t(carry) + uint16_t(x); + dst = uint8_t(res); + carry = res >> 8; +} + +FORCEINLINE void _addmul(uint8_t& carry, uint8_t& dstlo, uint8_t& dsthi, uint8_t a, uint8_t b) +{ + uint16_t res = uint16_t(a) * b; + uint8_t lo = uint8_t(res); + uint8_t hi = uint8_t(res >> 8); + carry = 0; + _adc(carry, dstlo, lo); + _adc(carry, dsthi, hi); +} + +int32_t fp1616_mul_CPP_test(int32_t a_, int32_t b_) +{ + uint8_t neg = 0; + if(a_ < 0) + { + a_ = -a_; + ++neg; + } + if(b_ < 0) + { + b_ = -b_; + ++neg; + } + + uint32_t a = a_; + uint32_t b = b_; + + uint8_t A = uint8_t(a); + uint8_t B = uint8_t(a >> 8); + uint8_t C = uint8_t(a >> 16); + uint8_t D = uint8_t(a >> 24); + + uint8_t X = uint8_t(b); + uint8_t Y = uint8_t(b >> 8); + uint8_t Z = uint8_t(b >> 16); + uint8_t W = uint8_t(b >> 24); + + uint8_t r0=0, r1=0, r2=0, r3=0, r4=0, r5=0, r6=0, r7=0, carry=0; + + if(A) + { + _addmul(carry, r0, r1, A, X); + _addmul(carry, r1, r2, A, Y); + _addmul(carry, r2, r3, A, Z); + _addmul(carry, r3, r4, A, W); + } + if(B) + { + _addmul(carry, r1, r2, B, X); + _addmul(carry, r2, r3, B, Y); + _addmul(carry, r3, r4, B, Z); + _addmul(carry, r4, r5, B, W); + } + if(C) + { + _addmul(carry, r2, r3, C, X); + _addmul(carry, r3, r4, C, Y); + _addmul(carry, r4, r5, C, Z); + _addmul(carry, r5, r6, C, W); + } + if(D) + { + _addmul(carry, r3, r4, D, X); + _addmul(carry, r4, r5, D, Y); + _addmul(carry, r5, r6, D, Z); + //_addmul(carry, r6, r7, D, W); + } + + int32_t res = (uint32_t(r2)) + | (uint32_t(r3) << 8) + | (uint32_t(r4) << 16) + | (uint32_t(r5) << 24); + + if(neg == 1) + res = -res; + + return res; +} + diff --git a/democode/src/demomath/_impl_cpp.h b/democode/src/demomath/_impl_cpp.h new file mode 100644 index 0000000..a1f2b1a --- /dev/null +++ b/democode/src/demomath/_impl_cpp.h @@ -0,0 +1,92 @@ +#pragma once + +inline static constexpr uint8_t avgfast_CPP(uint8_t a, uint8_t b) +{ + return (uint16_t(a)+b)/2u; +} + +inline static constexpr uint8_t avgfast4_CPP(uint8_t a, uint8_t b, uint8_t c, uint8_t d) +{ + return (uint16_t(a)+b+c+d)/4u; +} + +// unsigned +inline static constexpr uint8_t scale8_CPP(uint8_t i, uint8_t scale) +{ + return (((uint16_t)i) * (1+(uint16_t)(scale))) >> 8; +} + +// signed +inline static constexpr uint8_t scale8_CPP(int8_t i, uint8_t scale) +{ + return (((int16_t)i) * (1+(int16_t)(scale))) >> 8; +} + +// unsigned +inline static constexpr uint16_t scale16by8_CPP(uint16_t i, uint8_t scale) +{ + return scale + ? (i * (1+((uint16_t)scale))) >> 8 + : i; +} + +// signed +inline static constexpr int16_t scale16by8_CPP(int16_t i, uint8_t scale) +{ + // FIXME: THIS IS BROKEN ON AVR??! + return scale + ? (i * (1+((int16_t)scale))) >> 8 + : i; +} + +inline static constexpr uint16_t scale16_CPP(uint16_t i, uint16_t scale) +{ + return ((uint32_t)(i) * (1+(uint32_t)(scale))) / UINT32_C(65536); +} + +inline static constexpr uint8_t saturateAdd_CPP(uint8_t a, uint8_t b) +{ + return (uint8_t(a+b) < a) /* Can only happen due to overflow */ + ? uint8_t(0xff) + : uint8_t(a+b); +} + +inline static constexpr uint8_t saturateSub_CPP(uint8_t a, uint8_t b) +{ + return (uint8_t(a-b) > a) /* Can only happen due to overflow */ + ? uint8_t(0) + : uint8_t(a-b); +} + + +inline static constexpr int32_t fp1616_mul_CPP(int32_t a, int32_t b) +{ + return int32_t((int64_t(a) * int64_t(b)) >> int64_t(16)); +} + +inline static constexpr int16_t fp1616_intdiv_CPP(int32_t a, int32_t b) +{ + return int16_t(a / b); +} + +inline static constexpr int32_t fp1616_div_CPP(int32_t a, int32_t b) +{ + return int32_t((int64_t(a) << int64_t(16)) / int64_t(b)); +} + + + +inline static constexpr int16_t fp88_mul_CPP(int16_t a, int16_t b) +{ + return int16_t((int32_t(a) * int32_t(b)) >> int32_t(8)); +} + +inline static constexpr int8_t fp88_intdiv_CPP(int32_t a, int32_t b) +{ + return int8_t(a / b); +} + +inline static constexpr int16_t fp88_div_CPP(int16_t a, int16_t b) +{ + return int16_t((int32_t(a) << int32_t(8)) / int32_t(b)); +} diff --git a/democode/src/demomath/_impl_list.h b/democode/src/demomath/_impl_list.h new file mode 100644 index 0000000..9fb0c41 --- /dev/null +++ b/democode/src/demomath/_impl_list.h @@ -0,0 +1,23 @@ +#ifndef _IMPL +#error lolnope +#endif + +_IMPL(uint8_t, avgfast, (uint8_t a, uint8_t b), (a,b) ) +_IMPL(uint8_t, avgfast4, (uint8_t a, uint8_t b, uint8_t c, uint8_t d), (a,b,c,d)) +_IMPL(uint8_t, scale8, (uint8_t a, uint8_t b), (a,b) ) +_IMPL(int8_t, scale8, (int8_t a, uint8_t b), (a,b) ) +_IMPL(uint16_t, scale16by8, (uint16_t a, uint8_t b), (a,b) ) +_IMPL(int16_t, scale16by8, (int16_t a, uint8_t b), (a,b) ) +_IMPL(uint16_t, scale16, (uint16_t a, uint16_t b), (a,b) ) +_IMPL(uint8_t, saturateAdd, (uint8_t a, uint8_t b), (a,b) ) +_IMPL(uint8_t, saturateSub, (uint8_t a, uint8_t b), (a,b) ) + +_IMPL(int32_t, fp1616_mul, (int32_t a, int32_t b), (a,b) ) +_IMPL(int32_t, fp1616_intdiv, (int32_t a, int32_t b), (a,b) ) +_IMPL(int32_t, fp1616_div, (int32_t a, int32_t b), (a,b) ) + +_IMPL(int16_t, fp88_mul, (int16_t a, int16_t b), (a,b) ) +_IMPL(int16_t, fp88_intdiv, (int16_t a, int16_t b), (a,b) ) +_IMPL(int16_t, fp88_div, (int16_t a, int16_t b), (a,b) ) + +#undef _IMPL diff --git a/democode/src/demomath/_impl_master.cpp b/democode/src/demomath/_impl_master.cpp new file mode 100644 index 0000000..6a22488 --- /dev/null +++ b/democode/src/demomath/_impl_master.cpp @@ -0,0 +1 @@ +#include "_impl_master.h" diff --git a/democode/src/demomath/_impl_master.h b/democode/src/demomath/_impl_master.h new file mode 100644 index 0000000..1ccb478 --- /dev/null +++ b/democode/src/demomath/_impl_master.h @@ -0,0 +1,22 @@ +#pragma once + +#include "../demolib/cfg-demo.h" + +#ifdef FGLCD_ASM_AVR +# define _IMPL_ARCH _AVR +# include "_impl_avr.h" +#else +# define _IMPL_ARCH _CPP +# include "_impl_cpp.h" +#endif + +#define _JOIN2(a,b) a##b +#define _JOIN(a,b) _JOIN2(a,b) + +#define _IMPL(ty, fn, args, call) \ + static FORCEINLINE ty fn args \ + { return _JOIN(fn, _IMPL_ARCH) call; } +#include "_impl_list.h" + +#undef _JOIN +#undef _JOIN2 diff --git a/democode/src/demomath/camera.cpp b/democode/src/demomath/camera.cpp new file mode 100644 index 0000000..4dd7ce6 --- /dev/null +++ b/democode/src/demomath/camera.cpp @@ -0,0 +1,56 @@ +#define NO_SDL +#include "camera.h" +#include "fgmath.h" +#include + +Camera::Camera(uint8_t fov, unsigned w, unsigned h) +: projection(tweakedInfinitePerspective_InvAspect(fov, fp1616(int(h)) / fp1616(int(w)))) +, myup(0, -1, 0) +{ +} + +Camera::Camera(uint8_t fov) +: projection(tweakedInfinitePerspective_NoAspect(fov)) +, myup(0, -1, 0) +{ +} + +void Camera::lookAt(const vec3& at) +{ + myforward = v.getOrigin().to(at); + v.setForwardUp(myforward, myup); +} + +void Camera::setUp(const vec3 & up) +{ + myup = up; + v.setForwardUp(myforward, up); +} + +Camera::Matrix Camera::calcMatrix() const +{ + //return projection * LookAt(v.getOrigin(), at, v.getUp()); + + const vec3& me = v.getOrigin(); + const vec3& r = v.getRightNorm(); + const vec3& f = v.getForwardNorm(); + const vec3& u = v.getUpNorm(); + + tmat4_lookat look; +#define S(x, y, v) look.template set(v) + S(0, 0, r.x); + S(1, 0, r.y); + S(2, 0, r.z); + S(0, 1, u.x); + S(1, 1, u.y); + S(2, 1, u.z); + S(0, 2, -f.x); + S(1, 2, -f.y); + S(2, 2, -f.z); + S(3, 0, -dot(r, me)); + S(3, 1, -dot(u, me)); + S(3, 2, dot(f, me)); +#undef S + + return projection * look; +} diff --git a/democode/src/demomath/camera.h b/democode/src/demomath/camera.h new file mode 100644 index 0000000..d85eafd --- /dev/null +++ b/democode/src/demomath/camera.h @@ -0,0 +1,35 @@ +#pragma once + +#include "fgmath.h" +#include "mat4.h" +#include "view.h" + +class Camera +{ + typedef tmat4_persp ProjMatrix; + +public: + + Camera(uint8_t fov, unsigned w, unsigned h); + Camera(uint8_t fov); + FORCEINLINE void pos(const vec3& pos) { v.setOrigin(pos); } + void lookAt(const vec3& at); + + FORCEINLINE const vec3& getPos() const { return v.getOrigin(); } + + FORCEINLINE const vec3& getForwardNorm() const { return v.getForwardNorm(); } + FORCEINLINE const vec3& getUpNorm() const { return v.getUpNorm(); } + FORCEINLINE const vec3& getRightNorm() const { return v.getRightNorm(); } + + void setUp(const vec3& up); + + typedef decltype(ProjMatrix() * tmat4_lookat()) Matrix; + Matrix calcMatrix() const; + +public: + View v; + const ProjMatrix projection; + + vec3 myup, myforward; // not normalized +}; + diff --git a/democode/src/demomath/fast_hsv2rgb.h b/democode/src/demomath/fast_hsv2rgb.h new file mode 100644 index 0000000..5020498 --- /dev/null +++ b/democode/src/demomath/fast_hsv2rgb.h @@ -0,0 +1,207 @@ +// fg: got this via http://www.vagrearg.org/content/hsvrgb. hacked in shape a bit. + +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 B. Stultiens + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef __HSV_FAST_HSV2RGB_H__ +#define __HSV_FAST_HSV2RGB_H__ + +// fg: ADDED THIS +#ifdef __AVR__ +#define HSV_USE_ASSEMBLY +#ifndef __AVR_HAVE_MOVW__ +#error wat +#endif +#endif + +#include + +#define HSV_HUE_SEXTANT 256 +#define HSV_HUE_STEPS (6 * HSV_HUE_SEXTANT) + +#define HSV_HUE_MIN 0 +#define HSV_HUE_MAX (HSV_HUE_STEPS - 1) +#define HSV_SAT_MIN 0 +#define HSV_SAT_MAX 255 +#define HSV_VAL_MIN 0 +#define HSV_VAL_MAX 255 + +/* Options: */ +#define HSV_USE_SEXTANT_TEST /* Limit the hue to 0...360 degrees */ +//#define HSV_USE_ASSEMBLY /* Optimize code using assembly */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The "used" attribute is required in the assembly version because Arduino + * uses lto. With lto, the function is inlined into the rest of the code, which + * eliminates the pointers to r, g, and b. However, the code requires pointers + * because we need to do pointer swapping. + * Adding the attribute to the function here forces gcc/linker to add the + * function separately to the code and it must be called, just like other + * library functions. + */ +#ifdef HSV_USE_ASSEMBLY +#define HSVFUNC_ATTRUSED __attribute__((used)) +#else +#define HSVFUNC_ATTRUSED +#endif + +void fast_hsv2rgb_8bit(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g , uint8_t *b) HSVFUNC_ATTRUSED; +void fast_hsv2rgb_32bit(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g , uint8_t *b) HSVFUNC_ATTRUSED; + +#ifdef __cplusplus +} +#endif + + +/* + * Macros that are common to all implementations + */ +#define HSV_MONOCHROMATIC_TEST(s,v,r,g,b) \ + do { \ + if(!(s)) { \ + *(r) = *(g) = *(b) = (v); \ + return; \ + } \ + } while(0) + +#ifdef HSV_USE_SEXTANT_TEST +#define HSV_SEXTANT_TEST(sextant) \ + do { \ + if((sextant) > 5) { \ + (sextant) = 5; \ + } \ + } while(0) +#else +#define HSV_SEXTANT_TEST(sextant) do { ; } while(0) +#endif + +/* + * Pointer swapping: + * sext. r g b r<>b g<>b r <> g result + * 0 0 0 v u c !u v c u v c + * 0 0 1 d v c d v c + * 0 1 0 c v u u v c u v c + * 0 1 1 c d v v d c d v c d v c + * 1 0 0 u c v u v c u v c + * 1 0 1 v c d v d c d v c d v c + * + * if(sextant & 2) + * r <-> b + * + * if(sextant & 4) + * g <-> b + * + * if(!(sextant & 6) { + * if(!(sextant & 1)) + * r <-> g + * } else { + * if(sextant & 1) + * r <-> g + * } + */ +#define HSV_SWAPPTR(a,b) do { uint8_t *tmp = (a); (a) = (b); (b) = tmp; } while(0) +#define HSV_POINTER_SWAP(sextant,r,g,b) \ + do { \ + if((sextant) & 2) { \ + HSV_SWAPPTR((r), (b)); \ + } \ + if((sextant) & 4) { \ + HSV_SWAPPTR((g), (b)); \ + } \ + if(!((sextant) & 6)) { \ + if(!((sextant) & 1)) { \ + HSV_SWAPPTR((r), (g)); \ + } \ + } else { \ + if((sextant) & 1) { \ + HSV_SWAPPTR((r), (g)); \ + } \ + } \ + } while(0) + + +#if defined(HSV_USE_ASSEMBLY) + +#if defined(__AVR_HAVE_MUL__) +/* Multiply instruction available */ +#define MUL(ra,rb,pfx) \ + "mul " ra ", " rb "\n\t" +#else /* defined(__AVR_HAVE_MUL__) */ +/* + * Small AVR cores (f.x. ATtiny and ATmega*u2) do not have a mul instruction. + * It is available from the avr4 architecture. + * + * Multiply: r1:r0 = ra * rb (clobbers r19, r17, r16) + * Algorithm: + * uint16_t mul(uint8_t ra, uint8_t rb) + * { + * r1:r0 = 0 + * r19 = ra + * r17:r16 = rb + * do { + * if(r19 & 1) + * r1:r0 += r17:r16 + * r17:r16 += r17:r16 + * r19 >>= 1; + * } while(r19) + * return r1:r0 + * } + */ +#define MUL(ra,rb,pfx) \ + "\n" \ + ".L" pfx "mul_" ra "_" rb "_%=:\n\t" \ + "clr r0\n\t" \ + "clr r1\n\t" \ + "mov r19, " ra "\n\t" \ + "mov r16, " rb "\n\t" \ + "clr r17\n" \ + ".L" pfx "mul_loop_" ra "_" rb "_%=:\n\t" \ + "sbrc r19, 0\n\t" \ + "add r0, r16\n\t" \ + "sbrc r19, 0\n\t" \ + "adc r1, r17\n\t" \ + "add r16, r16\n\t" \ + "adc r17, r17\n\t" \ + "lsr r19\n\t" \ + "brne .L" pfx "mul_loop_" ra "_" rb "_%=\n\t" +#endif /* defined(__AVR_HAVE_MUL__) */ + +#if defined(__AVR_HAVE_MOVW__) +#define MOVW(rdh,rdl,rsh,rsl) \ + "movw " rdl ", " rsl "\n\t" +#else /* defined(__AVR_HAVE_MOVW__) */ +/* + * The avr2 (and avr1) architecture is missing the movw instruction + * (ATtiny22/26/1* and AT90s*). All others should have it. + */ +#define MOVW(rdh,rdl,rsh,rsl) \ + "mov " rdl ", " rsl "\n\t" \ + "mov " rdh ", " rsh "\n\t" +#endif /* defined(__AVR_HAVE_MOVW__) */ +#endif /* defined(HSV_USE_ASSEMBLY) */ + +#endif diff --git a/democode/src/demomath/fast_hsv2rgb_8bit.c b/democode/src/demomath/fast_hsv2rgb_8bit.c new file mode 100644 index 0000000..69b2f99 --- /dev/null +++ b/democode/src/demomath/fast_hsv2rgb_8bit.c @@ -0,0 +1,250 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 B. Stultiens + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "fast_hsv2rgb.h" + +#if defined(HSV_USE_ASSEMBLY) && !defined(__AVR_ARCH__) +#warning "Only AVR assembly is implemented. Other architectures use C fallback." +#undef HSV_USE_ASSEMBLY +#endif + +void fast_hsv2rgb_8bit(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g , uint8_t *b) +{ +#ifndef HSV_USE_ASSEMBLY + HSV_MONOCHROMATIC_TEST(s, v, r, g, b); // Exit with grayscale if s == 0 + + uint8_t sextant = h >> 8; + + HSV_SEXTANT_TEST(sextant); // Optional: Limit hue sextants to defined space + + HSV_POINTER_SWAP(sextant, r, g, b); // Swap pointers depending which sextant we are in + + *g = v; // Top level + + // Perform actual calculations + uint8_t bb; + uint16_t ww; + + /* + * Bottom level: v * (1.0 - s) + * --> (v * (255 - s) + error_corr) / 256 + */ + bb = ~s; + ww = v * bb; + ww += 1; // Error correction + ww += ww >> 8; // Error correction + *b = ww >> 8; + + uint8_t h_fraction = h & 0xff; // 0...255 + + if(!(sextant & 1)) { + // *r = ...slope_up...; + /* + * Slope up: v * (1.0 - s * (1.0 - h)) + * --> (v * (255 - (s * (256 - h) + error_corr1) / 256) + error_corr2) / 256 + */ + ww = !h_fraction ? ((uint16_t)s << 8) : (s * (uint8_t)(-h_fraction)); + ww += ww >> 8; // Error correction 1 + bb = ww >> 8; + bb = ~bb; + ww = v * bb; + ww += v >> 1; // Error correction 2 + *r = ww >> 8; + } else { + // *r = ...slope_down...; + /* + * Slope down: v * (1.0 - s * h) + * --> (v * (255 - (s * h + error_corr1) / 256) + error_corr2) / 256 + */ + ww = s * h_fraction; + ww += ww >> 8; // Error correction 1 + bb = ww >> 8; + bb = ~bb; + ww = v * bb; + ww += v >> 1; // Error correction 2 + *r = ww >> 8; + + /* + * A perfect match for h_fraction == 0 implies: + * *r = (ww >> 8) + (h_fraction ? 0 : 1) + * However, this is an extra calculation that may not be required. + */ + } + +#else /* HSV_USE_ASSEMBLY */ + +#ifdef __AVR_ARCH__ + /* + * Function arguments passed in registers: + * h = r25:r24 + * s = r22 + * v = r20 + * *r = r19:r18 + * *g = r17:r16 + * *b = r15:r14 + */ + asm volatile ( + MOVW("r27", "r26", "r19", "r18") // r -> X + MOVW("r29", "r28", "r17", "r16") // g -> Y + MOVW("r31", "r30", "r15", "r14") // b -> Z + + "cpse r22, __zero_reg__\n\t" // if(!s) --> monochromatic + "rjmp .Lneedcalc%=\n\t" + + "st X, r20\n\t" // *r = *g = *b = v; + "st Y, r20\n\t" + "st Z, r20\n\t" + "rjmp .Lendoffunc%=\n" // return + + ".Lneedcalc%=:\n\t" + + "cpi r25, lo8(6)\n\t" // if(hi8(h) > 5) hi8(h) = 5; + "brlo .Linrange%=\n\t" + "ldi r25,lo8(5)\n" + ".Linrange%=:\n\t" + + "sbrs r25, 1\n\t" // if(sextant & 2) swapptr(r, b); + "rjmp .Lsextno1%=\n\t" + MOVW("r19", "r18", "r27", "r26") + MOVW("r27", "r26", "r31", "r30") + MOVW("r31", "r30", "r19", "r18") + "\n" + ".Lsextno1%=:\n\t" + + "sbrs r25, 2\n\t" // if(sextant & 4) swapptr(g, b); + "rjmp .Lsextno2%=\n\t" + MOVW("r19", "r18", "r29", "r28") + MOVW("r29", "r28", "r31", "r30") + MOVW("r31", "r30", "r19", "r18") + "\n" + ".Lsextno2%=:\n\t" + + "ldi r18, lo8(6)\n\t" + "and r18, r25\n\t" // if(!(sextant & 6)) + "brne .Lsext2345%=\n\t" + + "sbrc r25, 0\n\t" // if(!(sextant & 6) && !(sextant & 1)) --> doswasp + "rjmp .Ldoneswap%=\n" + ".Lsext0%=:\n\t" + MOVW("r19", "r18", "r27", "r26") + MOVW("r27", "r26", "r29", "r28") + MOVW("r29", "r28", "r19", "r18") + "rjmp .Ldoneswap%=\n" + + ".Lsext2345%=:\n\t" + "sbrc r25, 0\n\t" // if((sextant & 6) && (sextant & 1)) --> doswap + "rjmp .Lsext0%=\n" + ".Ldoneswap%=:\n\t" + + /* Top level assignment first to free up Y register (r29:r28) */ + "st Y, r20\n\t" // *g = v + + "ldi r18, 0\n\t" // Temporary zero reg (r1 is used by mul) + "ldi r19, 1\n\t" // Temporary one reg + + /* + * Do bottom level next so we may use Z register (r31:r30). + * + * Bottom level: v * (1.0 - s) + * --> (v * (255 - s) + error_corr + 1) / 256 + * 1 bb = ~s; + * 2 ww = v * bb; + * 3 ww += 1; + * 4 ww += ww >> 8; // error_corr for division 1/256 instead of 1/255 + * 5 *b = ww >> 8; + */ + "mov r23, r22\n\t" // 1 use copy of s + "com r23\n\t" // 1 + MUL("r23", "r20", "a") // 2 r1:r0 = v * ~s + "add r0, r19\n\t" // 3 r1:r0 += 1 + "adc r1, r18\n\t" // 3 + "add r0, r1\n\t" // 4 r1:r0 += r1:r0 >> 8 + "adc r1, r18\n\t" // 4 + "st Z, r1\n\t" // 5 *b = r1:r0 >> 8 + + /* All that is left are the slopes */ + + "sbrc r25, 0\n\t" // if(sextant & 1) --> slope down + "rjmp .Lslopedown%=\n\t" + + /* + * Slope up: v * (1.0 - s * (1.0 - h)) + * --> (v * (255 - (s * (256 - h) + error_corr1) / 256) + error_corr2) / 256 + * 0 ww = 256 - h_fraction; + * 1 ww = s * bb; + * 2 ww += ww >> 8; // error_corr1 + * 3 bb = ww >> 8; // Implicit operation + * 4 bb = ~bb; + * 5 ww = v * bb; + * 6 ww += v >> 1; // error_corr2 + * 7 *r = ww >> 8; + */ + "ldi r28, 0\n\t" // 0 256 + "ldi r29, 1\n\t" + "sub r28, r24\n\t" // 0 256 - h_fraction + "sbc r29, r18\n\t" + MUL("r28", "r22", "b") // 1 r1:r0 = s * (256 - h_fraction) + "sbrc r29, 0\n\t" // 1 if(256 - h_fraction == 0x100) + "add r1, r22\n\t" // 1 r1:r0 += s << 8 + "rjmp .Lslopecommon%=\n\t" // r1:r0 holds inner multiplication + + /* + * Slope down: v * (1.0 - s * h) + * --> (v * (255 - (s * h + error_corr1) / 256) + error_corr2) / 256 + * 1 ww = s * h_fraction; + * 2 ww += ww >> 8; // error_corr1 + * 3 bb = ww >> 8; // Implicit operation + * 4 bb = ~bb; + * 5 ww = v * bb; + * 6 ww += v >> 1; // error_corr2 + * 7 *r = ww >> 8; + */ + "\n" + ".Lslopedown%=:\n\t" + MUL("r24", "r22", "c") // 1 r1:r0 = s * h_fraction + "\n" + ".Lslopecommon%=:\n\t" + "add r0, r1\n\t" // 2 error_corr1 + "adc r1, r18\n\t" // 2 + "com r1\n\t" // 4 bb = ~bb + MUL("r1", "r20", "d") // 5 r1:r0 = v * bb + "lsr r20\n\t" // 6 error_corr2: v >>= 1 + "add r0, r20\n\t" // 6 r1:r0 += v >> 1 + "adc r1, r18\n" // 6 + "st X, r1\n\t" // 7 *r = slope result + + "clr __zero_reg__\n" // Restore zero reg + + ".Lendoffunc%=:\n" + : + : + : "r31", "r30", "r29", "r28", "r27", "r26", "r25", "r24", "r22", "r19", "r18" +#ifndef __AVR_HAVE_MUL__ + , "r17", "r16" +#endif + ); +#else /* __AVR_ARCH__ */ +#error "No assembly version implemented for architecture" +#endif +#endif /* HSV_USE_ASSEMBLY */ +} diff --git a/democode/src/demomath/fgmath.h b/democode/src/demomath/fgmath.h new file mode 100644 index 0000000..8b2c4f0 --- /dev/null +++ b/democode/src/demomath/fgmath.h @@ -0,0 +1,6 @@ +#pragma once + +// LEGACY HEADER + +#include "mathfuncs.h" + diff --git a/democode/src/demomath/fp16_16.h b/democode/src/demomath/fp16_16.h new file mode 100644 index 0000000..df06820 --- /dev/null +++ b/democode/src/demomath/fp16_16.h @@ -0,0 +1,133 @@ +#pragma once + +#include "fp8_8.h" +#include "_impl_master.h" + +struct fp1616 +{ + typedef uint8_t u8; + typedef int8_t s8; + typedef uint16_t u16; + typedef uint32_t u32; + typedef int16_t s16; + typedef int32_t s32; + + struct _raw_tag {}; + + // These UINTMAX_C shenanigans are because avr-gcc flattens 0xffff+1 to 0 ?? + // Also see https://godbolt.org/z/ZVYuTY + static const constexpr float MANTISSA_DIV = float(INTMAX_C(0xffff)+INTMAX_C(1)); + + // Iiii iiii iiii iiii mmmm mmmm mmmm mmmm, two's complement + union layout + { + s32 i; + struct { u16 lo; s16 hi; } part; + + FORCEINLINE layout() {} + FORCEINLINE constexpr layout(s16 realpart, u16 mantissa) + : i((u32(realpart) << u32(16)) | mantissa) + {} + FORCEINLINE constexpr layout(float in) + : i((in < 0) ? -_fromfloat(-in) : _fromfloat(in)) + {} + FORCEINLINE constexpr layout(u32 raw, _raw_tag) + : i(raw) + {} + + private: + static constexpr s32 _fromfloat(float a) + { + return _mantissa(a) | (u32(int(a)) << u32(16)); + } + static constexpr u16 _mantissa(float a) + { + return u16((a - float(int(a))) * MANTISSA_DIV); + } + } f; + + FORCEINLINE fp1616() {} + FORCEINLINE constexpr fp1616(u32 x, _raw_tag) : f(x, _raw_tag()) {} + FORCEINLINE fp1616(const fp1616& in) : f(in.f.i, _raw_tag()) {} + FORCEINLINE fp1616(const fp1616&& in) : f(in.f.i, _raw_tag()) {} + FORCEINLINE constexpr fp1616(const layout& in) : f(in) {} + //FORCEINLINE fp1616(s16 in) : f(in, 0) {} + FORCEINLINE constexpr fp1616(float in) : f(in) {} + FORCEINLINE constexpr fp1616(int in) : f(in, 0) {} + FORCEINLINE constexpr fp1616(unsigned in) : f((int)in, 0) {} + FORCEINLINE fp1616(const fp88& in) : f(s16(in.intpart()), u16(in.mantissa()) << 8u) {} + static FORCEINLINE constexpr fp1616 raw(u32 in) { return fp1616(in, _raw_tag()); } + static FORCEINLINE constexpr fp1616 raw(s16 i, u16 m) { return fp1616(layout(i, m)); } + FORCEINLINE fp1616& operator=(const fp1616& o) { f.i = o.f.i; return *this; } + template + FORCEINLINE fp1616 operator*(const T& o) const { fp1616 t = *this; t.mul(o); return t; } + template + FORCEINLINE fp1616& operator*=(const T& o) { mul(o); return *this;} + + // kludge to allow compile-time constexpr multiplication so we don't have to touch op*. + // gcc has __builtin_constant_p() but there's no MSVC equivalent and std::is_constant_evaluated() is still a long way off. + static constexpr fp1616 cmul(const fp1616& a, const fp1616& b) + { + return raw(fp1616_mul_CPP(a.f.i, b.f.i)); + } + + FORCEINLINE constexpr fp1616 operator+(const fp1616& o) const { return raw(f.i + o.f.i); } + FORCEINLINE fp1616& operator+=(const fp1616& o) { f.i += o.f.i; return *this;} + FORCEINLINE constexpr fp1616 operator-(const fp1616& o) const { return raw(f.i - o.f.i); } + FORCEINLINE fp1616& operator-=(const fp1616& o) { f.i -= o.f.i; return *this;} + FORCEINLINE fp1616 operator-() const { return raw(-f.i); } + FORCEINLINE fp1616 operator+() const { return *this; } + template + FORCEINLINE fp1616 operator/(const T& o) const { fp1616 t = *this; t.div(o); return t; } + template + FORCEINLINE fp1616& operator/=(const T& o) { div(o); return *this; } + FORCEINLINE bool operator!() const { return !f.i; } + + FORCEINLINE bool operator==(const fp1616& o) const { return f.i == o.f.i; } + FORCEINLINE bool operator!=(const fp1616& o) const { return f.i != o.f.i; } + FORCEINLINE bool operator< (const fp1616& o) const { return f.i < o.f.i; } + FORCEINLINE bool operator<=(const fp1616& o) const { return f.i <= o.f.i; } + FORCEINLINE bool operator> (const fp1616& o) const { return f.i > o.f.i; } + FORCEINLINE bool operator>=(const fp1616& o) const { return f.i >= o.f.i; } + + FORCEINLINE constexpr u32 raw() const { return f.i; } + FORCEINLINE constexpr u16 mantissa() const { return f.part.lo; } + FORCEINLINE constexpr s16 intpart() const { return f.part.hi; } + + FORCEINLINE float tofloat() const { return float(f.i) / MANTISSA_DIV; } + + FORCEINLINE fp88 tofp88() const { return fp88((s8)ulo8(intpart()), uhi8(mantissa())); } + + void mul(const fp1616& o) + { + f.i = fp1616_mul(f.i, o.f.i); + } + // FIXME: optimized mul with fp8.8 + void mul(const fp88& o) + { + mul(fp1616(o)); + } + + template + FORCEINLINE void mul(const T& o) + { + f.i *= o; + } + + void div(const fp1616& o) + { + f.i = fp1616_div(f.i, o.f.i); + } + template + FORCEINLINE void div(const T& o) + { + f.i /= o; + } + + FORCEINLINE int16_t int16div(const fp1616& by) const + { + return fp1616_intdiv(f.i, by.f.i); + } +}; + + diff --git a/democode/src/demomath/fp8_8.h b/democode/src/demomath/fp8_8.h new file mode 100644 index 0000000..c782eb9 --- /dev/null +++ b/democode/src/demomath/fp8_8.h @@ -0,0 +1,109 @@ +#pragma once + +// only intended for storage, NOT for everyday math! + +#include "_impl_master.h" + +#include "../demolib/demo_def.h" + +struct fp88 +{ + typedef uint8_t u8; + typedef int8_t s8; + typedef uint16_t u16; + typedef uint32_t u32; + typedef int16_t s16; + typedef int32_t s32; + + static const constexpr float MANTISSA_DIV = float(INTMAX_C(0xff)+INTMAX_C(1)); + + // Iiii iiii iiii mmmm, two's complement + union layout + { + s16 i; + struct { u8 lo; s8 hi; } part; + + FORCEINLINE layout() {} + FORCEINLINE constexpr layout(s8 realpart, u8 mantissa) + : i((u16(realpart) << u16(8)) | mantissa) + {} + FORCEINLINE constexpr layout(float in) + : i((in < 0) ? -_fromfloat(-in) : _fromfloat(in)) + {} + FORCEINLINE constexpr layout(u16 raw) + : i(raw) + {} + + private: + static constexpr s16 _fromfloat(float a) + { + return _mantissa(a) | (u8(int(a)) << s16(8)); + } + static constexpr u8 _mantissa(float a) + { + return u8((a - float(int(a))) * MANTISSA_DIV); + } + } f; + + struct _raw_tag {}; + + FORCEINLINE fp88() {} + FORCEINLINE constexpr fp88(u16 x, _raw_tag) : f(x) {} + FORCEINLINE fp88(const fp88& in) : f(in.f) {} + FORCEINLINE fp88(const fp88&& in) : f(in.f) {} + FORCEINLINE constexpr fp88(const layout& in) : f(in) {} + FORCEINLINE constexpr fp88(s8 in) : f(in, 0) {} + FORCEINLINE constexpr fp88(s8 in, u8 mantissa) : f(in, mantissa) {} + FORCEINLINE constexpr fp88(float in) : f(in) {} + FORCEINLINE constexpr fp88(int in) : f(in, 0) {} + static FORCEINLINE constexpr fp88 raw(u16 in) { return fp88(in, _raw_tag()); } + FORCEINLINE fp88& operator=(const fp88& o) { f.i = o.f.i; return *this; } + template + FORCEINLINE fp88 operator*(const T& o) const { fp88 t = *this; t.mul(o); return t; } + template + FORCEINLINE fp88& operator*=(const T& o) { mul(o); return *this;} + FORCEINLINE fp88 operator+(const fp88& o) const { fp88 r = f; r.f.i += o.f.i; return r; } + FORCEINLINE fp88& operator+=(const fp88& o) { f.i += o.f.i; return *this;} + FORCEINLINE fp88 operator-(const fp88& o) const { fp88 r = f; r.f.i -= o.f.i; return r; } + FORCEINLINE fp88& operator-=(const fp88& o) { f.i -= o.f.i; return *this;} + FORCEINLINE fp88 operator-() const { fp88 o; o.f.i = -f.i; return o; } + FORCEINLINE fp88 operator+() const { return *this; } + template + FORCEINLINE fp88 operator/(const T& o) const { fp88 t = *this; t.div(o); return t; } + template + FORCEINLINE fp88& operator/=(const T& o) { div(o); return *this;} + + FORCEINLINE bool operator==(const fp88& o) const { return f.i == o.f.i; } + FORCEINLINE bool operator!=(const fp88& o) const { return f.i != o.f.i; } + FORCEINLINE bool operator< (const fp88& o) const { return f.i < o.f.i; } + FORCEINLINE bool operator<=(const fp88& o) const { return f.i <= o.f.i; } + FORCEINLINE bool operator> (const fp88& o) const { return f.i > o.f.i; } + FORCEINLINE bool operator>=(const fp88& o) const { return f.i >= o.f.i; } + + FORCEINLINE u8 mantissa() const { return f.part.lo; } + FORCEINLINE s8 intpart() const { return f.part.hi; } + //FORCEINLINE operator s8() const { return intpart(); } + //FORCEINLINE operator float() const { return tofloat(); } + FORCEINLINE float tofloat() const { return float(f.i) / MANTISSA_DIV; } + FORCEINLINE uint16_t raw() const { return f.i; } + + void mul(const fp88& o) + { + f.i = fp88_mul(f.i, o.f.i); + } + + template + FORCEINLINE void mul(const T& o) + { + f.i *= o; + } + void div(const fp88& o) + { + f.i = fp88_div(f.i, o.f.i); + } + template + FORCEINLINE void div(const T& o) + { + f.i /= o; + } +}; diff --git a/democode/src/demomath/lookup.cpp b/democode/src/demomath/lookup.cpp new file mode 100644 index 0000000..83cc85f --- /dev/null +++ b/democode/src/demomath/lookup.cpp @@ -0,0 +1,25 @@ +#define NO_SDL + +#include "lookup.h" +#include "../demolib/demo_def.h" +#include "../demolib/fglcd/fglcd.h" + +static fp88 readtanval(fglcd::FarPtr p, uint8_t i) +{ + uint8_t idx = i & 0x7f; + uint16_t rawval = fglcd::ProgmemFar::template read(p, idx); + return fp88::raw(rawval) * fp88::raw(0x91); +} + + +fp88 tan88slow(uint8_t x) +{ + fglcd::FarPtr tab = fglcd_get_farptr(lut_tantab); + return readtanval(tab, x); +} + +fp88 invtan88slow(uint8_t x) +{ + fglcd::FarPtr tab = fglcd_get_farptr(lut_invtantab); + return readtanval(tab, x); +} diff --git a/democode/src/demomath/lookup.h b/democode/src/demomath/lookup.h new file mode 100644 index 0000000..7be8a56 --- /dev/null +++ b/democode/src/demomath/lookup.h @@ -0,0 +1,15 @@ +#pragma once + +#include "fp8_8.h" +#include "fp16_16.h" +#include "staticdata.h" + + +static int8_t isin8slow(uint8_t x) { return fglcd::ProgmemFar::template read(fglcd_get_farptr(lut_isintab), x); } +static uint8_t usin8slow(uint8_t x) { return fglcd::ProgmemFar::template read(fglcd_get_farptr(lut_isintab), x) + uint8_t(127); } + +static int8_t icos8slow(uint8_t x) { return fglcd::ProgmemFar::template read(fglcd_get_farptr(lut_isintab), uint8_t(x + 64)); } +static uint8_t ucos8slow(uint8_t x) { return fglcd::ProgmemFar::template read(fglcd_get_farptr(lut_isintab), uint8_t(x + 64)) + uint8_t(127); } + +fp88 tan88slow(uint8_t x); +fp88 invtan88slow(uint8_t x); diff --git a/democode/src/demomath/mat4.h b/democode/src/demomath/mat4.h new file mode 100644 index 0000000..6cd6a1a --- /dev/null +++ b/democode/src/demomath/mat4.h @@ -0,0 +1,746 @@ +#pragma once + +#include "typelist.h" +#include "vec.h" + +template +struct tmat4; + +namespace detail { + + +enum ElemDef +{ + E_ZERO, + E_ONE, + E_DYNAMIC, + E_MINUSONE, + + E0 = E_ZERO, + E1 = E_ONE, + ED = E_DYNAMIC, + EM = E_MINUSONE +}; + +enum ElemSel +{ + S_CONST, + S_FIRST, + S_SECOND, + S_BOTH, + S_NEGATE_FIRST, // compiler can't optimize this because we're using custom types with inline asm + S_NEGATE_SECOND // ^ +}; + +template struct ElemDefType : fgstd::IntegralConstant {}; +typedef ElemDefType ElemDynamicType; + +template +struct ElemInfo : fgstd::IntegralConstant +{ +}; + +template +struct ElemInfo : fgstd::IntegralConstant +{ + enum : uint8_t { DynamicIndex = dynidx }; +}; + + +template +struct _MakeElemInfo +{ + static const ElemDef defval = E::value; + typedef ElemInfo type; + typedef fgstd::IntegralConstant state; +}; + +template +struct ElemValueHelper {}; + +template +struct ElemValueHelper { static FORCEINLINE constexpr T const_value() { return T(0); } }; +template +struct ElemValueHelper { static FORCEINLINE constexpr T const_value() { return T(1); } }; +template +struct ElemValueHelper { static FORCEINLINE constexpr T const_value() { return T(-1); } }; + +template +struct ElemOpHelper {}; + +template +struct ElemOpHelper +{ + template + static FORCEINLINE constexpr typename OP::template result_type::type op(const A& a, const B& b) + { + return OP::template op(a, b); + } +}; + +template +struct ElemOpHelper +{ + static_assert(def == E_DYNAMIC, "should be E_DYNAMIC"); + template + static FORCEINLINE constexpr const A& op(const A& a, const B&) + { + return a; + } +}; + +template +struct ElemOpHelper +{ + static_assert(def == E_DYNAMIC, "should be E_DYNAMIC"); + template + static FORCEINLINE constexpr const B& op(const A&, const B& b) + { + return b; + } +}; + +template +struct ElemOpHelper +{ + static_assert(def == E_DYNAMIC, "should be E_DYNAMIC"); + template + static FORCEINLINE constexpr const A op(const A& a, const B&) + { + return -a; + } +}; + +template +struct ElemOpHelper +{ + static_assert(def == E_DYNAMIC, "should be E_DYNAMIC"); + template + static FORCEINLINE constexpr const B op(const A&, const B& b) + { + return -b; + } +}; + +template +struct ElemOpHelper +{ + template + static FORCEINLINE constexpr auto op(const A&, B&) -> decltype(ElemValueHelper::type>::const_value()) + { + return ElemValueHelper::type>::const_value(); + } +}; + +template +struct ElemOpDef : public ElemDefType +{ + static const ElemSel which = sel; +}; + +template struct ElemAddBase : ElemOpDef +{ + template + struct result_type + { + typedef decltype(A() + B()) type; + }; + + template + static FORCEINLINE constexpr auto op(const A& a, const B& b) -> decltype(a*b) + { + return a + b; + } +}; +template struct ElemAdd {}; +template<> struct ElemAdd : ElemAddBase {}; +template<> struct ElemAdd : ElemAddBase {}; +template<> struct ElemAdd : ElemAddBase {}; +template<> struct ElemAdd : ElemAddBase {}; +template<> struct ElemAdd : ElemAddBase {}; +template<> struct ElemAdd : ElemAddBase {}; // TODO: ? +template<> struct ElemAdd : ElemAddBase {}; +template<> struct ElemAdd : ElemAddBase {}; +template<> struct ElemAdd : ElemAddBase {}; +template<> struct ElemAdd : ElemAddBase {}; +template<> struct ElemAdd : ElemAddBase {}; +template<> struct ElemAdd : ElemAddBase {}; +template<> struct ElemAdd : ElemAddBase {}; +template<> struct ElemAdd : ElemAddBase {}; +template<> struct ElemAdd : ElemAddBase {}; +template<> struct ElemAdd : ElemAddBase {}; + +template struct ElemMulBase : ElemOpDef +{ + template + struct result_type + { + typedef decltype(A() * B()) type; + }; + + template + static FORCEINLINE constexpr auto op(const A& a, const B& b) -> decltype(a*b) + { + return a * b; + } +}; +template struct ElemMul {}; +template<> struct ElemMul : ElemMulBase {}; +template<> struct ElemMul : ElemMulBase {}; +template<> struct ElemMul : ElemMulBase {}; +template<> struct ElemMul : ElemMulBase {}; +template<> struct ElemMul : ElemMulBase {}; +template<> struct ElemMul : ElemMulBase {}; +template<> struct ElemMul : ElemMulBase {}; +template<> struct ElemMul : ElemMulBase {}; +template<> struct ElemMul : ElemMulBase {}; +template<> struct ElemMul : ElemMulBase {}; +template<> struct ElemMul : ElemMulBase {}; +template<> struct ElemMul : ElemMulBase {}; +template<> struct ElemMul : ElemMulBase {}; +template<> struct ElemMul : ElemMulBase {}; +template<> struct ElemMul : ElemMulBase {}; +template<> struct ElemMul : ElemMulBase {}; + +template +struct getelem +{ + static_assert(EINFO::value == def, "plz"); + template + FORCEINLINE static constexpr T get(const T *) { return ElemValueHelper::const_value(); } +}; + +template +struct getelem +{ + static_assert(EINFO::value == E_DYNAMIC, "must be E_DYNAMIC"); + template + FORCEINLINE static T get(const T *p) + { + return p[EINFO::DynamicIndex]; + } +}; + +template +struct setelem {}; + +template +struct setelem +{ + static_assert(EINFO::value == E_DYNAMIC, "must be E_DYNAMIC"); + template + FORCEINLINE static void set(T *p, const T& v) + { + p[EINFO::DynamicIndex] = v; + } +}; + +template +struct mat4def +{ +#define E(x) fgstd::IntegralConstant + typedef FGSTD_TYPELIST((E(A0), E(A1), E(A2), E(A3), E(A4), E(A5), E(A6), E(A7), E(A8), E(A9), E(A10), E(A11), E(A12), E(A13), E(A14), E(A15))) rawTL; +#undef E + typedef typename rawTL::template transform<_MakeElemInfo, fgstd::IntegralConstant > _TLtrans; + typedef typename _TLtrans::type TL; + typedef typename _TLtrans::state TLstate; + enum { NumDynamic = TLstate::value }; + + template + struct gettype + { + enum { listidx = y * 4 + x }; + typedef typename TL::template get::type type; + }; + + template + struct accessor + { + typedef typename gettype::type etype; + typedef getelem getter; + + template + FORCEINLINE static constexpr T get(const T *p) { return getter::get(p); } + }; + + template + struct setvalue + { + typedef typename gettype::type etype; + typedef setelem setter; + + template + FORCEINLINE static void set(T *p, const T& v) { setter::set(p, v); } + }; +}; + +template +struct mat4op_mul_row_col_rec +{ + static_assert(MA::is_matrix_check, "not matrix"); + static_assert(MB::is_matrix_check, "not matrix"); + + typedef typename MA::template accessor aa; + typedef typename MB::template accessor ab; + typedef typename aa::etype aty; + typedef typename ab::etype bty; + typedef ElemMul Mul; + static const ElemDef value = Mul::value; + static const ElemSel sel = Mul::which; + typedef ElemOpHelper Op; + typedef typename Mul::template result_type::type result_type; + + static FORCEINLINE result_type getvalue(const MA& a, const MB& b) + { + const typename MA::value_type va = a.template get(); + const typename MB::value_type vb = b.template get(); + return Op::op(va, vb); + } +}; + +template +struct mat4op_mul_row_col +{ + static_assert(MA::is_matrix_check, "not matrix"); + static_assert(MB::is_matrix_check, "not matrix"); + + typedef mat4op_mul_row_col_rec m0; + typedef mat4op_mul_row_col_rec m1; + typedef mat4op_mul_row_col_rec m2; + typedef mat4op_mul_row_col_rec m3; + static const ElemDef v0 = m0::value; + static const ElemDef v1 = m1::value; + static const ElemDef v2 = m2::value; + static const ElemDef v3 = m3::value; + typedef ElemAdd a0; + static const ElemDef tmp1 = a0::value; + typedef ElemAdd a1; + static const ElemDef tmp2 = a1::value; + typedef ElemAdd a2; + static const ElemDef value = a2::value; + typedef typename a2::template result_type::type result_type; + + static const ElemSel sel0 = a0::which; + static const ElemSel sel1 = a1::which; + static const ElemSel sel2 = a2::which; + typedef ElemOpHelper Op0; + typedef ElemOpHelper Op1; + typedef ElemOpHelper Op2; + + static FORCEINLINE result_type getvalue(const MA& a, const MB& b) + { + //return m0::getvalue(a,b) + m1::getvalue(a,b) + m2::getvalue(a,b) + m3::getvalue(a,b); + + const typename m0::result_type mv0 = m0::getvalue(a,b); + const typename m1::result_type mv1 = m1::getvalue(a,b); + const typename m2::result_type mv2 = m2::getvalue(a,b); + const typename m3::result_type mv3 = m3::getvalue(a,b); + const result_type av0 = Op0::op(mv0, mv1); + const result_type av1 = Op1::op(av0, mv2); + const result_type av2 = Op2::op(av1, mv3); + return av2; + } +}; + +template +struct mat4_mul_filler2 +{ + template + static FORCEINLINE void apply(R& m, const MA& a, const MB& b) {} +}; + +template +struct mat4_mul_filler2 +{ + template + static FORCEINLINE void apply(R& m, const MA& a, const MB& b) + { + typedef mat4op_mul_row_col getter; + m.template set(getter::getvalue(a, b)); + } +}; + +template +struct mat4_mul_filler +{ + static const uint8_t x = idx % 4; + static const uint8_t y = idx / 4; + static_assert(x < 4 && y < 4, "wat"); + template + static FORCEINLINE void apply(R& m, const MA& a, const MB& b) + { + typedef mat4op_mul_row_col getter; + mat4_mul_filler2::template apply(m, a, b); + mat4_mul_filler::template apply(m, a, b); + } +}; +template<> +struct mat4_mul_filler<16> +{ + template + static FORCEINLINE void apply(R& m, const MA& a, const MB& b) {} +}; + +template +struct mat4op_mul +{ + static_assert(fgstd::is_same::value, "matrix must have same value_type"); // TODO: relax this? + template struct elem : mat4op_mul_row_col {}; +#define E(x,y) (elem::value) +#define EROW(y) E(0,y), E(1,y), E(2,y), E(3,y) + typedef mat4def deftype; +#undef E +#undef EROW + + template using resulttype = tmat4; + + static resulttype getresult(const MA& a, const MB& b) + { + static_assert(MA::is_matrix_check, "not matrix"); + static_assert(MB::is_matrix_check, "not matrix"); + typedef resulttype mtype; + static_assert(mtype::is_matrix_check, "result should be matrix"); + mtype m; + mat4_mul_filler<0>::template apply(m, a, b); + /*if((m.template get<3, 3>()) <= mtype::Zero()) { + //printf("invalid w value: %i\n", m.template get<3, 3>()); + assert(false); + }*/ + return m; + } +}; + +template +struct mat4op_mul_vec4_rec +{ + static_assert(M::is_matrix_check, "not matrix"); + + typedef typename M::mvec4 mvec4; + typedef typename M::template accessor aa; + typedef typename aa::etype aty; + typedef ElemMul Mul; + static const ElemDef value = Mul::value; + static const ElemSel sel = Mul::which; + typedef ElemOpHelper Op; + typedef typename Mul::template result_type::type result_type; + + template + static FORCEINLINE result_type getvalue(const M& a, const tvec4& v) + { + const typename M::value_type va = a.template get(); + return Op::op(va, v[x]); + } +}; + +template +struct mat4op_mul_vec4_elem +{ + static_assert(M::is_matrix_check, "not matrix"); + + typedef typename M::mvec4 mvec4; + typedef mat4op_mul_vec4_rec m0; + typedef mat4op_mul_vec4_rec m1; + typedef mat4op_mul_vec4_rec m2; + typedef mat4op_mul_vec4_rec m3; + static const ElemDef v0 = m0::value; + static const ElemDef v1 = m1::value; + static const ElemDef v2 = m2::value; + static const ElemDef v3 = m3::value; + typedef ElemAdd a0; + static const ElemDef tmp1 = a0::value; + typedef ElemAdd a1; + static const ElemDef tmp2 = a1::value; + typedef ElemAdd a2; + static const ElemDef value = a2::value; + typedef typename a2::template result_type::type result_type; + + static const ElemSel sel0 = a0::which; + static const ElemSel sel1 = a1::which; + static const ElemSel sel2 = a2::which; + typedef ElemOpHelper Op0; + typedef ElemOpHelper Op1; + typedef ElemOpHelper Op2; + + template + static FORCEINLINE result_type getvalue(const M& a, const tvec4& v) + { + //return m0::getvalue(a,b) + m1::getvalue(a,b) + m2::getvalue(a,b) + m3::getvalue(a,b); + + const typename m0::result_type mv0 = m0::getvalue(a,v); + const typename m1::result_type mv1 = m1::getvalue(a,v); + const typename m2::result_type mv2 = m2::getvalue(a,v); + const typename m3::result_type mv3 = m3::getvalue(a,v); + const result_type av0 = Op0::op(mv0, mv1); + const result_type av1 = Op1::op(av0, mv2); + const result_type av2 = Op2::op(av1, mv3); + return av2; + } +}; + + +template +struct mat4op_mul_vec4 +{ + typedef typename M::mvec4 mvec4; + typedef mat4op_mul_vec4_elem e0; + typedef mat4op_mul_vec4_elem e1; + typedef mat4op_mul_vec4_elem e2; + typedef mat4op_mul_vec4_elem e3; + template + static mvec4 getresult(const M& m, const tvec4& v) + { + mvec4 r(noinit); + r[0] = e0::getvalue(m, v); + r[1] = e1::getvalue(m, v); + r[2] = e2::getvalue(m, v); + r[3] = e3::getvalue(m, v); + return r; + } +}; + +typedef mat4def< E1, E0, E0, E0, + E0, E1, E0, E0, + E0, E0, E1, E0, + E0, E0, E0, E1 > def_identity; + +typedef mat4def< ED, ED, ED, ED, + ED, ED, ED, ED, + ED, ED, ED, ED, + ED, ED, ED, ED > def_fulldynamic; + +typedef mat4def< ED, E0, E0, E0, + E0, ED, E0, E0, + E0, E0, ED, E0, + E0, E0, E0, E1 > def_scale; + +typedef mat4def< E1, E0, E0, E0, + E0, E1, E0, E0, + E0, E0, E1, E0, + E0, E0, E0, ED > def_downscale; + +typedef mat4def< E1, E0, E0, ED, + E0, E1, E0, ED, + E0, E0, E1, ED, + E0, E0, E0, E1 > def_translate; + +typedef mat4def< E1, E0, E0, E0, + E0, ED, ED, E0, + E0, ED, ED, E0, + E0, E0, E0, E1 > def_rotx; + +typedef mat4def< ED, E0, ED, E0, + E0, E1, E0, E0, + ED, E0, ED, E0, + E0, E0, E0, E1 > def_roty; + +typedef mat4def< ED, ED, E0, E0, + ED, ED, E0, E0, + E0, E0, E1, E0, + E0, E0, E0, E1 > def_rotz; + +typedef mat4def< ED, ED, ED, E0, + ED, ED, ED, E0, + ED, ED, ED, E0, + E0, E0, E0, E1 > def_rotxyz; + +typedef mat4def< ED, ED, ED, ED, + ED, ED, ED, ED, + ED, ED, ED, ED, + E0, E0, E0, EM > def_lookat; // HACK: last elem should be 1, but -1 fixes inverted-Z + +typedef mat4def< ED, E0, E0, E0, + E0, ED, E0, E0, + E0, E0, EM, EM, + E0, E0, ED, E0 > def_persp; + +typedef mat4def< ED, E0, E0, E0, + E0, ED, E0, E0, + E0, E0, E1, ED, + E0, E0, ED, E0 > def_infperspLH; + + +} // end namespace detail + +template +struct tmat4; + +template +struct tmat4_any : public tmat4 +{ +}; + +template +struct tmat4_ident : public tmat4 +{ +}; + +template +struct tmat4 +{ + static const bool is_matrix_check = true; + typedef tmat4 Self; + typedef T value_type; + typedef DEF Def; + typedef typename Def::TL Infolist; + typedef tvec4 mvec4; + + static FORCEINLINE T Zero() { return T(0); } + static FORCEINLINE T One() { return T(1); } + + enum { dynamicsize = Def::NumDynamic }; + T _dyn[dynamicsize]; + + template + struct accessor : Def::template accessor {}; + + template + FORCEINLINE T get() const + { + typedef accessor acc; + return acc::get(_dyn); + } + + template + struct setter : Def::template setvalue {}; + + template + FORCEINLINE void set(const T& v) + { + typedef setter sett; + sett::set(_dyn, v); + } + + template + FORCEINLINE typename fgstd::enable_if::template resulttype >::type operator*(const OM& o) const + { + return detail::mat4op_mul::getresult(*this, o); + } + + template + FORCEINLINE mvec4 operator*(const tvec4& v) const + { + return detail::mat4op_mul_vec4::getresult(*this, v); + } + + /* FIXME THIS IS SOMEHOW BROKEN (still uninited values in ret?!) + tmat4_any getfull() const + { + tmat4_any ret; +#define $(x, y) ret.template set(this->template get()); +#define $$(r) $(0, r); $(1, r); $(2, 0); $(3, 0); + $$(0); $$(1); $$(2); $$(3) +#undef $$ +#undef $ + return ret; + } + */ + + template + FORCEINLINE void rawload(typename Mem::Pointer p) + { + Mem::Memcpy(&_dyn[0], p, sizeof(_dyn)); + } +}; + +template +struct tmat4_trans : public tmat4 +{ + typedef tmat4 Base; + + FORCEINLINE tmat4_trans(const T& x, const T& y, const T& z) + { + this->template set<3, 0>(x); + this->template set<3, 1>(y); + this->template set<3, 2>(z); + } +}; + +template +struct tmat4_scale : public tmat4 +{ + FORCEINLINE tmat4_scale(const T& x) + { + this->template set<0, 0>(x); + this->template set<1, 1>(x); + this->template set<2, 2>(x); + } + + FORCEINLINE tmat4_scale(const tvec3& v) + { + this->template set<0, 0>(v.x); + this->template set<1, 1>(v.y); + this->template set<2, 2>(v.z); + } +}; + +template +struct tmat4_downscale : public tmat4 +{ + FORCEINLINE tmat4_downscale(const T& x) + { + this->template set<3, 3>(x); + } +}; + +template +struct tmat4_rotx : public tmat4 +{ + template + tmat4_rotx(const typename SINCOS::angle_type& a, const SINCOS& lut) + { + const T c = lut.cos(a); + const T s = lut.sin(a); + this->template set<1, 1>(c); + this->template set<2, 2>(c); + this->template set<1, 2>(s); + this->template set<2, 1>(-s); + }; +}; + +template +struct tmat4_roty : public tmat4 +{ + template + tmat4_roty(const typename SINCOS::angle_type& a, const SINCOS& lut) + { + const T c = lut.cos(a); + const T s = lut.sin(a); + this->template set<0, 0>(c); + this->template set<2, 2>(c); + this->template set<2, 0>(s); + this->template set<0, 2>(-s); + }; +}; + +template +struct tmat4_rotz : public tmat4 +{ + template + tmat4_rotz(const typename SINCOS::angle_type& a, const SINCOS& lut) + { + const T c = lut.cos(a); + const T s = lut.sin(a); + this->template set<0, 0>(c); + this->template set<1, 1>(c); + this->template set<0, 1>(s); + this->template set<1, 0>(-s); + }; +}; + +template +struct tmat4_lookat : public tmat4 +{ +}; + +template +struct tmat4_infperspLH : public tmat4 +{ +}; + +template +struct tmat4_persp : public tmat4 +{ +}; diff --git a/democode/src/demomath/mathfuncs.cpp b/democode/src/demomath/mathfuncs.cpp new file mode 100644 index 0000000..ded2122 --- /dev/null +++ b/democode/src/demomath/mathfuncs.cpp @@ -0,0 +1,167 @@ +#define NO_SDL + +#include "fgmath.h" +#include "lookup.h" + +// via https://github.com/chmike/fpsqrt/blob/master/fpsqrt.c +fp1616 sqrt(fp1616 x) +{ + uint32_t r = x.raw(); + uint32_t b = 0x40000000; + uint32_t q = 0; + while( b > 0x40 ) + { + uint32_t t = q + b; + if( r >= t ) + { + r -= t; + q = t + b; // equivalent to q += 2*b + } + r <<= 1; + b >>= 1; + } + q >>= 8; + return fp1616::raw(q); +} + +uint8_t sqrt16(uint16_t A) +{ + uint8_t bit = 0x80; + uint8_t x = 0; + while(bit) + { + x += bit; + if(A < uint16_t(x) * x) + x -= bit; + bit >>= 1; + } + return x; +} + +// might check https://stackoverflow.com/questions/6286450/inverse-sqrt-for-fixed-point eventually, +// but that didn't work +fp1616 invsqrt(fp1616 input) +{ + return input.f.i ? fp1616(1) / sqrt(input) : 0; +} + +static const constexpr float NEAR = 0.01f; +static const constexpr float FAR = 100; + +static const constexpr fp1616 near = fp1616(NEAR); +static const constexpr fp1616 invnear = fp1616(1.0f / NEAR); +static const constexpr fp1616 one = fp1616(1); +static const constexpr fp1616 minusone = fp1616(-1); +static const constexpr fp1616 two = fp1616(2); +static const constexpr fp1616 half = fp1616(0.5f); +static const constexpr fp1616 twonear = fp1616(NEAR * 2); +static const constexpr fp1616 eps = fp1616::raw(1); +static const constexpr fp1616 epsMinusOne = eps - 1; +static const constexpr fp1616 epsMinusTwo = eps - 2; +static const constexpr fp1616 epsMinusTwoTimesNear = fp1616::cmul(epsMinusTwo, near); //fp1616::raw(0xffffe668); // (eps - 2) * near + +// sucks. fucked up. +/* +tmat4_persp perspective(uint8_t fovy, fp1616 aspect) +{ + const float tanHalfFovy = tan(fovy); //tan88slow(fovy).tofloat(); + const fp1616 scale = aspect / tanHalfFovy; + + tmat4_persp r; + //r.template set<0, 0>(1.0f / (aspect.tofloat() * tanHalfFovy)); + //r.template set<1, 1>(1.0f / (tanHalfFovy)); + r.template set<0, 0>(scale); + r.template set<1, 1>(scale); + //r.template set<3, 2>(-1); // encoded in type + r.template set<2, 2>(- FAR / (NEAR - FAR)); + r.template set<2, 3>(-(FAR * NEAR) / (FAR - NEAR)); + //r.template set<2, 2>(0); // can just 0 since we don't need the depth, apparently... so no more near/far as well + //r.template set<3, 2>(0); + return r; +} +*/ +#if 1 +// NO-DIVISION TEMP VERSION +tmat4_persp tweakedInfinitePerspective(uint8_t fovy, fp1616 aspect) +{ + const fp1616 itanv = fp1616(invtan88slow(fovy)); + const fp1616 irange = invnear * itanv; + const fp1616 tmp = near * irange; + + tmat4_persp r; + r.template set<1, 1>(tmp); + r.template set<0, 0>(tmp * (one / aspect)); // TODO: should be done by caller + //r.template set<2, 2>(epsMinusOne); // close enough to -1, also encoded in type + //r.template set<3, 2>(minusone); // encoded in type + r.template set<2, 3>(epsMinusTwoTimesNear); + return r; +} + +#else +// SLOW, GOOD VERSION +tmat4_persp tweakedInfinitePerspective(uint8_t fovy, fp1616 aspect) +{ + const fp1616 tanv = fp1616(tan88slow(fovy)); + const fp1616 range = near * tanv; + const fp1616 tmp = range * aspect; + const fp1616 span = tmp + tmp; + + tmat4_persp r; + r.template set<0, 0>(twonear / span); + r.template set<1, 1>(twonear / (range + range)); + //r.template set<2, 2>(epsMinusOne); // close enough to -1, also encoded in type + //r.template set<3, 2>(minusone); // encoded in type + r.template set<2, 3>(epsMinusTwoTimesNear); + return r; +} +#endif + +tmat4_persp tweakedInfinitePerspective_InvAspect(uint8_t fovy, fp1616 iaspect) +{ + const fp1616 itanv = fp1616(invtan88slow(fovy)); + const fp1616 irange = invnear * itanv; + const fp1616 tmp = near * irange; + + tmat4_persp r; + r.template set<1, 1>(tmp); + r.template set<0, 0>(tmp * iaspect); // TODO: should be done by caller + //r.template set<2, 2>(epsMinusOne); // close enough to -1, also encoded in type + //r.template set<3, 2>(minusone); // encoded in type + r.template set<2, 3>(epsMinusTwoTimesNear); + return r; +} + +tmat4_persp tweakedInfinitePerspective_NoAspect(uint8_t fovy) +{ + const fp1616 itanv = fp1616(invtan88slow(fovy)); + + tmat4_persp r; + r.template set<1, 1>(itanv); + r.template set<0, 0>(itanv); + //r.template set<2, 2>(epsMinusOne); // close enough to -1, also encoded in type + //r.template set<3, 2>(minusone); // encoded in type + r.template set<2, 3>(epsMinusTwoTimesNear); + return r; +} + +// NOT TESTED YET +/* +tmat4_infperspLH infinitePerspectiveLH(uint8_t fovy, fp1616 aspect) +{ + static constexpr fp1616 near = fp1616(NEAR); + static constexpr fp1616 twonear = fp1616(NEAR * 2); + static constexpr fp1616 minustwonear = fp1616(NEAR * -2); + + const fp88 tanv = tan88slow(fovy); + const fp1616 range = near * fp1616(tanv); + const fp1616 tmp = range * aspect; + const fp1616 span = tmp + tmp; + + tmat4_infperspLH r; + r.template set<0, 0>(twonear / span); + r.template set<1, 1>(twonear / (range + range)); + r.template set<3, 2>(1); // this fixes everything backwards + r.template set<2, 3>(minustwonear); + return r; +} +*/ diff --git a/democode/src/demomath/mathfuncs.h b/democode/src/demomath/mathfuncs.h new file mode 100644 index 0000000..e3fa57f --- /dev/null +++ b/democode/src/demomath/mathfuncs.h @@ -0,0 +1,296 @@ +#pragma once + +#include "mathtypes.h" +#include "_impl_master.h" + +template static constexpr FORCEINLINE T vmax(T a, T b) { return a < b ? b : a; } +template static constexpr FORCEINLINE T vmin(T a, T b) { return a < b ? a : b; } +template static constexpr FORCEINLINE T vclamp(T v, T mi, T ma) { return vmax(vmin(v, ma), mi); } +template static constexpr FORCEINLINE T vmin3(T a, T b, T c) { return vmin(a, vmin(b, c)); } +template static constexpr FORCEINLINE T vmax3(T a, T b, T c) { return vmax(a, vmax(b, c)); } +template static constexpr FORCEINLINE T vmin4(T a, T b, T c, T d) { return vmin(vmin(a, d), vmin(b, c)); } +template static constexpr FORCEINLINE T vmax4(T a, T b, T c, T d) { return vmax(vmax(a, d), vmax(b, c)); } +template static constexpr FORCEINLINE T vabs(T a) { return a >= T(0) ? a : -a; } +template static constexpr FORCEINLINE T vsgn(T a) { return a ? (a >= T(0) ? 1 : -1) : 0; } + +template static FORCEINLINE void vswap(T& a, T& b) { T tmp = a; a = b; b = tmp; } +template static FORCEINLINE void makeminmax(T& a, T& b) { if(b < a) vswap(a, b); } + +template +static FORCEINLINE void sort3(T& a, T& b, T& c) +{ + if(a > c) + vswap(a, c); + if(a > b) + vswap(a, b); + if(b > c) + vswap(b, c); +} + +template +struct NextPowerOf2 +{ +private: + enum Tmp : uint64_t + { + v0 = in - 1, + v1 = v0 | (v0 >> 1), + v2 = v1 | (v1 >> 2), + v3 = v2 | (v2 >> 4), + v4 = v3 | (v3 >> 8), + v5 = v4 | (v4 >> 16), + v6 = v5 | (v5 >> 32), + v7 = v6 + 1 + }; +public: + enum Result { value = T(v7) }; +}; +static_assert(NextPowerOf2::value == 0, "pow2"); +static_assert(NextPowerOf2::value == 1, "pow2"); +static_assert(NextPowerOf2::value == 2, "pow2"); +static_assert(NextPowerOf2::value == 4, "pow2"); +static_assert(NextPowerOf2::value == 4, "pow2"); +static_assert(NextPowerOf2::value == 8, "pow2"); +static_assert(NextPowerOf2::value == 8, "pow2"); + +// wrap-around modulo without using operator%. +// value stays always positive or 0 +template static T vmodpos(T val, T mod) +{ + FGLCD_ASSERT(mod != 0, "mod 0"); + + while(val >= mod) + val -= mod; + + while(val < T(0)) + val += mod; + + return val; +} + +template static FORCEINLINE T vmodpos(T val) +{ + return vmodpos(val, mod); +} + +template +struct _MoreBits {}; + +template<> struct _MoreBits { typedef uint16_t type; FORCEINLINE static type convert(uint8_t v) { return v; }}; +template<> struct _MoreBits { typedef uint32_t type; FORCEINLINE static type convert(uint16_t v) { return v; }}; +template<> struct _MoreBits { typedef uint64_t type; FORCEINLINE static type convert(uint32_t v) { return v; }}; +template<> struct _MoreBits { typedef int16_t type; FORCEINLINE static type convert(int8_t v) { return v; }}; +template<> struct _MoreBits { typedef int32_t type; FORCEINLINE static type convert(int16_t v) { return v; }}; +template<> struct _MoreBits { typedef int64_t type; FORCEINLINE static type convert(int32_t v) { return v; }}; + +template +FORCEINLINE static typename _MoreBits::type morebits(T x) { return _MoreBits::convert(x); } + +// BEWARE: T must be an unsigned type! +template +FORCEINLINE T saturateAdd(T a, T b) +{ + static_assert(T(-1) > T(0), "signed types are UB here"); + T c = a + b; + if (c < a) /* Can only happen due to overflow */ + c = T(-1); + return c; +} + +template +FORCEINLINE T saturateSub(T a, T b) +{ + static_assert(T(-1) > T(0), "signed types are UB here"); + T c = a - b; + if (c > a) /* Can only happen due to overflow */ + c = 0; + return c; +} + +template +static FORCEINLINE B hiscale8(B b, C c) +{ + static_assert(sizeof(B) == 1, "expected 8 bit type"); + static_assert(sizeof(C) == 1, "expected 8 bit type"); + return (B)uhi8(b * morebits(c)); +} + +// Function useful for scaling values +template +static FORCEINLINE void incrhiscale(T& a, B b, C c) +{ + a += hiscale8(b, c); +} + + +template +struct GCD +{ + static constexpr uintmax_t largevalue = GCD::value; + typedef typename fglcd::TypeForSize::type value_type; + static constexpr value_type value = largevalue; +}; +template +struct GCD +{ + static constexpr uintmax_t largevalue = A; + typedef typename fglcd::TypeForSize::type value_type; + static constexpr value_type value = largevalue; +}; + + +template +struct LCM +{ + static constexpr uintmax_t largevalue = (A*B) / GCD::largevalue; + typedef typename fglcd::TypeForSize::type value_type; + static constexpr value_type value = largevalue; +}; + +/* +template +tmat4_lookat LookAt(const tvec3& me, const tvec3& dst, const tvec3& up) +{ + const tvec3 f(normalize(dst - me)); + const tvec3 s(normalize(cross(f, up))); + const tvec3 u(cross(s, f)); + + tmat4_lookat r; +#define S(x, y, v) r.template set(v) + S(0, 0, s.x); + S(1, 0, s.y); + S(2, 0, s.z); + S(0, 1, u.x); + S(1, 1, u.y); + S(2, 1, u.z); + S(0, 2, -f.x); + S(1, 2, -f.y); + S(2, 2, -f.z); + S(3, 0, -dot(s, me)); + S(3, 1, -dot(u, me)); + S(3, 2, dot(f, me)); +#undef S + return r; +}; +*/ + +tmat4_persp tweakedInfinitePerspective(uint8_t fovy, fp1616 aspect); +tmat4_persp tweakedInfinitePerspective_InvAspect(uint8_t fovy, fp1616 iaspect); +tmat4_persp tweakedInfinitePerspective_NoAspect(uint8_t fovy); +tmat4_persp perspective(uint8_t fovy, fp1616 aspect); +tmat4_infperspLH infinitePerspectiveLH(uint8_t fovy, fp1616 aspect); + + +template +struct Ilog2Trec +{ + enum { value = Ilog2Trec<(V >> 1u), L+1>::value }; +}; + +template +struct Ilog2Trec<0, L> +{ + enum { value = L }; +}; + +template +struct Ilog2T +{ + enum { value = Ilog2Trec::value - 1 }; +}; + +fp1616 sqrt(fp1616 x); +fp1616 invsqrt(fp1616 x); + +template +T scaleT(T a, F t); + +template<> FORCEINLINE +uint8_t scaleT(uint8_t a, uint8_t t) +{ + return scale8(a, t); +} + +template<> FORCEINLINE +uint16_t scaleT(uint16_t a, uint8_t t) +{ + return scale16by8(a, t); +} +template<> FORCEINLINE +int16_t scaleT(int16_t a, uint8_t t) +{ + return scale16by8(a, t); +} + +template<> FORCEINLINE +uint16_t scaleT(uint16_t a, uint16_t t) +{ + return scale16(a, t); +} + +// TODO: too lazy for an optimized 16x8 impl right now +template<> FORCEINLINE +uint8_t scaleT(uint8_t a, uint16_t t) +{ + return (uint8_t)scale16(a, t); +} + +/* +inline uint8_t brighten8(uint8_t x) +{ + uint8_t ix = 255 - x; + return 255 - scale8( ix, ix); +} + +inline uint8_t dim8(uint8_t x) +{ + return scale8(x, x); +} +*/ + +template +FORCEINLINE static T lerp_inl(T a, T b, F frac) +{ + T result; + if(b > a) { + T delta = b - a; + T scaled = scaleT(delta, frac); + result = a + scaled; + } else { + T delta = a - b; + T scaled = scaleT(delta, frac); + result = a - scaled; + } + return result; +} + +template +NOINLINE static T lerp(T a, T b, F frac) +{ + return lerp_inl(a, b, frac); +} + +template +NOINLINE static V vlerp(const V& a, const V& b, F frac) +{ + V ret(noinit); + for(vecitype i = 0; i < V::elems; ++i) + ret[i] = lerp(a[i], b[i], frac); + return ret; +} + +template +FORCEINLINE tvec2 lerp(const tvec2& a, const tvec2& b, F frac) +{ + return vlerp(a, b, frac); +} +template +FORCEINLINE tvec3 lerp(const tvec3& a, const tvec3& b, F frac) +{ + return vlerp(a, b, frac); +} +template +FORCEINLINE tvec4 lerp(const tvec4& a, const tvec4& b, F frac) +{ + return vlerp(a, b, frac); +} diff --git a/democode/src/demomath/mathtypes.h b/democode/src/demomath/mathtypes.h new file mode 100644 index 0000000..4b17ef0 --- /dev/null +++ b/democode/src/demomath/mathtypes.h @@ -0,0 +1,45 @@ +#include "vec.h" +#include "fp16_16.h" +#include "fp8_8.h" +#include "mat4.h" + +// Runtime representation +typedef tvec2 vec2; +typedef tvec3 vec3; +typedef tvec4 vec4; + +// “Short vec”, i.e. storage format +typedef tvec2 svec2; +typedef tvec3 svec3; +typedef tvec4 svec4; + +// float vectors, used by the demotools for generating integer models +typedef tvec2 fvec2; +typedef tvec3 fvec3; +typedef tvec4 fvec4; + +// Integer vectors, used for screen coordinates +typedef tvec2 ivec2; +typedef tvec3 ivec3; +typedef tvec4 ivec4; + +typedef tvec2 i32vec2; +typedef tvec3 i32vec3; +typedef tvec4 i32vec4; + +typedef tvec2 uvec2; +typedef tvec3 uvec3; +typedef tvec4 uvec4; + +typedef tvec2 u8vec2; +typedef tvec3 u8vec3; +typedef tvec4 u8vec4; + +typedef tmat4_any mat4; +typedef tmat4_downscale mat4ds; +typedef tmat4_ident mat4i; +typedef tmat4_scale mat4s; +typedef tmat4_trans mat4t; +typedef tmat4_rotx mat4rx; +typedef tmat4_roty mat4ry; +typedef tmat4_rotz mat4rz; diff --git a/democode/src/demomath/staticdata.cpp b/democode/src/demomath/staticdata.cpp new file mode 100644 index 0000000..07a4909 --- /dev/null +++ b/democode/src/demomath/staticdata.cpp @@ -0,0 +1,65 @@ +#define NO_SDL + +#include "staticdata.h" +#include "../demolib/demo_def.h" + +// TODO: this can be shrunk to 64 bytes +const int8_t lut_isintab[] PROGMEM_FAR = +{ +0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 59, 62, +65, 67, 70, 73, 75, 78, 80, 82, 85, 87, 89, 91, 94, 96, 98, 100, 102, 103, 105, 107, +108, 110, 112, 113, 114, 116, 117, 118, 119, 120, 121, 122, 123, 123, 124, 125, 125, +126, 126, 126, 126, 126, 127, 126, 126, 126, 126, 126, 125, 125, 124, 123, 123, 122, +121, 120, 119, 118, 117, 116, 114, 113, 112, 110, 108, 107, 105, 103, 102, 100, 98, +96, 94, 91, 89, 87, 85, 82, 80, 78, 75, 73, 70, 67, 65, 62, 59, 57, 54, 51, 48, 45, +42, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, -3, -6, -9, -12, -15, -18, +-21, -24, -27, -30, -33, -36, -39, -42, -45, -48, -51, -54, -57, -59, -62, -65, -67, +-70, -73, -75, -78, -80, -82, -85, -87, -89, -91, -94, -96, -98, -100, -102, -103, +-105, -107, -108, -110, -112, -113, -114, -116, -117, -118, -119, -120, -121, -122, +-123, -123, -124, -125, -125, -126, -126, -126, -126, -126, -127, -126, -126, -126, +-126, -126, -125, -125, -124, -123, -123, -122, -121, -120, -119, -118, -117, -116, +-114, -113, -112, -110, -108, -107, -105, -103, -102, -100, -98, -96, -94, -91, -89, +-87, -85, -82, -80, -78, -75, -73, -70, -67, -65, -62, -59, -57, -54, -51, -48, -45, +-42, -39, -36, -33, -30, -27, -24, -21, -18, -15, -12, -9, -6, -3, +}; + +const uint8_t lut_tantab[] PROGMEM_FAR = +{ // mult = 0x91 +0,0,11,0,22,0,33,0,44,0,55,0,66,0,77,0, +89,0,100,0,112,0,124,0,136,0,148,0,160,0,173,0, +185,0,199,0,212,0,226,0,240,0,254,0,13,1,28,1, +44,1,60,1,77,1,94,1,112,1,131,1,150,1,171,1, +193,1,215,1,239,1,8,2,35,2,63,2,93,2,125,2, +160,2,197,2,237,2,24,3,72,3,124,3,181,3,245,3, +60,4,140,4,230,4,79,5,200,5,86,6,0,7,207,7, +209,8,27,10,211,11,56,14,207,17,199,23,180,35,115,71, +0,0,141,184,76,220,57,232,49,238,200,241,45,244,229,245, +47,247,49,248,0,249,170,249,56,250,177,250,26,251,116,251, +196,251,11,252,75,252,132,252,184,252,232,252,19,253,59,253, +96,253,131,253,163,253,193,253,221,253,248,253,17,254,41,254, +63,254,85,254,106,254,125,254,144,254,162,254,179,254,196,254, +212,254,228,254,243,254,2,255,16,255,30,255,44,255,57,255, +71,255,83,255,96,255,108,255,120,255,132,255,144,255,156,255, +167,255,179,255,190,255,201,255,212,255,223,255,234,255,245,255 +}; + + +const uint8_t lut_invtantab[] PROGMEM_FAR = +{ // mult = 0x91 +0,0,115,71,180,35,199,23,207,17,56,14,211,11,27,10, +209,8,207,7,0,7,86,6,200,5,79,5,230,4,140,4, +60,4,245,3,181,3,124,3,72,3,24,3,237,2,197,2, +160,2,125,2,93,2,63,2,35,2,8,2,239,1,215,1, +193,1,171,1,150,1,131,1,112,1,94,1,77,1,60,1, +44,1,28,1,13,1,254,0,240,0,226,0,212,0,199,0, +185,0,173,0,160,0,148,0,136,0,124,0,112,0,100,0, +89,0,77,0,66,0,55,0,44,0,33,0,22,0,11,0, +0,0,245,255,234,255,223,255,212,255,201,255,190,255,179,255, +167,255,156,255,144,255,132,255,120,255,108,255,96,255,83,255, +71,255,57,255,44,255,30,255,16,255,2,255,243,254,228,254, +212,254,196,254,179,254,162,254,144,254,125,254,106,254,85,254, +63,254,41,254,17,254,248,253,221,253,193,253,163,253,131,253, +96,253,59,253,19,253,232,252,184,252,132,252,75,252,11,252, +196,251,116,251,26,251,177,250,56,250,170,249,0,249,49,248, +47,247,229,245,45,244,200,241,49,238,57,232,76,220,141,184, +}; diff --git a/democode/src/demomath/staticdata.h b/democode/src/demomath/staticdata.h new file mode 100644 index 0000000..1fce395 --- /dev/null +++ b/democode/src/demomath/staticdata.h @@ -0,0 +1,7 @@ +#pragma once + +#include "../demolib/demo_def.h" + +extern const int8_t lut_isintab[] PROGMEM_FAR; +extern const uint8_t lut_tantab[] PROGMEM_FAR; +extern const uint8_t lut_invtantab[] PROGMEM_FAR; diff --git a/democode/src/demomath/tinyrng.h b/democode/src/demomath/tinyrng.h new file mode 100644 index 0000000..d6d6df3 --- /dev/null +++ b/democode/src/demomath/tinyrng.h @@ -0,0 +1,46 @@ +#pragma once + +#include + + +// Adapted from http://www.arklyffe.com/main/2010/08/29/xorshift-pseudorandom-number-generator/ + +class TinyRng8 +{ +public: + TinyRng8() : _state((uint8_t)(uintptr_t)this) {} + TinyRng8(uint8_t x) : _state(x) {} + inline void init(uint8_t x) { _state = x; } + inline void mix(uint8_t x) { _state += x; } + uint8_t operator()() + { + uint8_t s = _state; + s ^= (s << 1); + s ^= (s >> 1); + s ^= (s << 2); + _state = s; + return s - 1; + } +private: + uint8_t _state; +}; + +class TinyRng16 +{ +public: + TinyRng16() : _state((uint16_t)(uintptr_t)this) {} + TinyRng16(uint16_t x) : _state(x) {} + inline void init(uint16_t x) { _state = x; } + inline void mix(uint16_t x) { _state += x; } + uint16_t operator()() + { + uint16_t s = _state; + s ^= (s << 1); + s ^= (s >> 1); + s ^= (s << 14); + _state = s; + return s - 1; + } +private: + uint16_t _state; +}; diff --git a/democode/src/demomath/traits.h b/democode/src/demomath/traits.h new file mode 100644 index 0000000..498244c --- /dev/null +++ b/democode/src/demomath/traits.h @@ -0,0 +1,224 @@ +#pragma once + +#ifdef __GNUC__ +#define FGSTD_FORCE_INLINE __attribute__((always_inline)) inline +#elif defined(_MSC_VER) +#define FGSTD_FORCE_INLINE __forceinline +#else +#define FGSTD_FORCE_INLINE inline +#endif + +#define FGSTD_USE_CPP11 + +namespace fgstd { + +template +struct IntegralConstant +{ + typedef T value_type; + typedef IntegralConstant type; + static const T value = v; +}; + +typedef IntegralConstant CompileTrue; +typedef IntegralConstant CompileFalse; +template struct CompileCheck : IntegralConstant{}; + +template struct enable_if { }; +template struct enable_if { typedef T type; }; + +template struct is_same; +template struct is_same : CompileFalse { }; +template struct is_same : CompileTrue { }; + +template struct is_array : CompileFalse {}; +template struct is_array : CompileTrue {}; +template struct is_array : CompileTrue{}; + +template struct is_reference : CompileFalse {}; +template struct is_reference : CompileTrue{}; +#ifdef FGSTD_USE_CPP11 +template struct is_reference : CompileTrue{}; +#endif + +template struct is_ptr : CompileFalse {}; +template struct is_ptr : CompileTrue{}; + +//template struct is_same_value : priv::IntegralConstant; + +template struct TypeSwitch{}; +template struct TypeSwitch { typedef A type; }; +template struct TypeSwitch { typedef B type; }; + +template struct remove_ref { typedef T type; }; +template struct remove_ref { typedef T type; }; +#ifdef FGSTD_USE_CPP11 +template struct remove_ref { typedef T type; }; +#endif + +template struct remove_const { typedef T type; }; +template struct remove_const { typedef T type; }; + +template struct remove_volatile { typedef T type; }; +template struct remove_volatile { typedef T type; }; + +template struct remove_ptr { typedef T type; }; +template struct remove_ptr { typedef T type; }; + +template struct remove_array { typedef T type; }; +template struct remove_array { typedef typename remove_array::type type; }; +template struct remove_array { typedef typename remove_array::type type; }; + + +template struct remove_cv +{ + typedef typename remove_const< + typename remove_volatile::type + >::type type; +}; + +template struct _get_basic_type_rec {}; +template struct _get_basic_type_rec { typedef T type; }; +template struct _get_basic_type_rec { + typedef + typename remove_array< + typename remove_ref< + typename remove_cv< + T + >::type + >::type + >::type + X; + + typedef typename _get_basic_type_rec< + is_array::value || is_reference::value || is_ptr::value, + X + >::type type; +}; + +template struct basic_type : _get_basic_type_rec {}; + + + +#ifdef FGSTD_USE_CPP11 +template +typename remove_ref::type&& move (T&& x) +{ + return static_cast::type &&>(x); +} +#define FGSTD_MOVE(x) fgstd::move(x) +#else +#define FGSTD_MOVE(x) x +#endif + + +namespace detail +{ + +template +struct has_swap_method +{ + typedef char yes[1]; + typedef char no[2]; + template struct SFINAE {}; + template static yes& Test(SFINAE*); + template static no& Test(...); + enum { value = sizeof(Test(0)) == sizeof(yes) }; +}; + +template +struct _swapper; + +template<> +struct _swapper +{ + template + static FGSTD_FORCE_INLINE void swap(T& a, T& b) + { + a.swap(b); + } +}; + +template<> +struct _swapper +{ + template + static FGSTD_FORCE_INLINE void swap(T& a, T& b) + { + T c(FGSTD_MOVE(a)); + a = FGSTD_MOVE(b); + b = FGSTD_MOVE(c); + } +}; + +} // end namespace detail + + +template +FGSTD_FORCE_INLINE void swap(T& a, T& b) +{ + detail::_swapper::value>::swap(a, b); +} + +template +struct swapper +{ + FGSTD_FORCE_INLINE void operator()(T& a, T& b) const + { + fgstd::swap(a, b); + } +}; +template<> +struct swapper +{ + template + FGSTD_FORCE_INLINE void operator()(T& a, T& b) const + { + fgstd::swap(a, b); + } +}; + +template +void iter_swap(const ITER& a, const ITER& b) +{ + swap(*a, *b); +} + + +template +struct less +{ + FGSTD_FORCE_INLINE bool operator()(const T& a, const T& b) const + { + return a < b; + } +}; +template<> +struct less +{ + template + FGSTD_FORCE_INLINE bool operator()(const T& a, const T& b) const + { + return a < b; + } +}; + +template +struct equal +{ + FGSTD_FORCE_INLINE bool operator()(const T& a, const T& b) const + { + return a == b; + } +}; +template<> +struct equal +{ + template + FGSTD_FORCE_INLINE bool operator()(const T& a, const T& b) const + { + return a == b; + } +}; + +} diff --git a/democode/src/demomath/typelist.h b/democode/src/demomath/typelist.h new file mode 100644 index 0000000..d7eafb3 --- /dev/null +++ b/democode/src/demomath/typelist.h @@ -0,0 +1,236 @@ +#pragma once + +#include "traits.h" + +namespace fgstd { + +template +struct ValToType { static constexpr T value = v; }; + +struct typelist_end; + +template +class typelist; + +struct typelist_end +{ + enum { length = 0 }; + typedef void maxalign_type; + typedef void Head; + static size_t array_req_bytes(size_t) { return 0; } + + template + struct cons + { + typedef typelist type; + }; +}; + +template +struct _typelist_get +{ + typedef typename _typelist_get::type type; +}; + +template +struct _typelist_get +{ + typedef HEAD type; +}; + +template +struct _typelist_count +{ + enum { value = !!is_same::value + _typelist_count::value }; +}; + +template +struct _typelist_count +{ + enum { value = !!is_same::value }; +}; + +template +struct _typelist_reverse +{ + typedef typename _typelist_reverse, typename TAIL::Head, typename TAIL::Tail>::type type; +}; + +template +struct _typelist_reverse +{ + typedef typelist type; +}; + +template +struct _typelist_concat +{ + typedef typename _typelist_concat::type newtail; + typedef typename typelist::type type; +}; + +template +struct _typelist_concat +{ + typedef typelist type; +}; + +// first parameter is the thing to transform, the rest is optional and just passed through +template class X, typename... Ps> +struct _single_transform +{ + typedef X transformed; +}; +template class X, typename STATE, typename HEAD, typename TAIL, typename... Ps> +struct _list_transform +{ + typedef typename _single_transform::transformed transformed; + typedef typename transformed::type newtype; + typedef typename transformed::state newstate; + typedef _list_transform transtail; + typedef typename transtail::type tailtype; + typedef typename transtail::state tailstate; + typedef typelist type; + typedef tailstate state; +}; +template class X, typename STATE, typename HEAD, typename... Ps> +struct _list_transform +{ + typedef typename _single_transform::transformed transformed; + typedef typename transformed::type newtype; + typedef typename transformed::state newstate; + typedef typelist type; + typedef typename transformed::state state; +}; +template class X, typename STATE, typename... Ps> +struct _list_transform +{ + typedef typelist_end type; + typedef STATE state; +}; + + +template +struct elemvalue +{ + static constexpr T value = E::value; +}; + +template +struct elemcastvalue +{ + static constexpr T value = static_cast(E::value); +}; + +template +struct array +{ + enum { size = sizeof...(vals) }; + static constexpr T values[size] = { vals... }; +}; + +template class X, typename HEAD, typename TAIL, T... vals> +struct arraybuilder +{ + static constexpr T _value = X::value; + typedef typename arraybuilder::type type; +}; +template class X, typename HEAD, T... vals> +struct arraybuilder +{ + static constexpr T _value = X::value; + typedef array type; +}; +template class X, T... vals> +struct arraybuilder +{ + typedef array type; +}; + +template +class typelist +{ +public: + typedef HEAD Head; + typedef TAIL Tail; + + template + struct get + { + typedef typename _typelist_get::type type; + }; + + template + struct count + { + enum { value = _typelist_count::value }; + }; + + template + struct cons + { + typedef typelist > type; + }; + + struct reverse + { + typedef typename _typelist_reverse::type type; + }; + + template + struct append + { + typedef typename _typelist_concat::type type; + }; + + template class X, typename BEGINSTATE, typename... Ps> + struct transform + { + typedef _list_transform _final; + typedef typename _final::type type; + typedef typename _final::state state; + }; + + template class X = elemvalue> + struct toarray : public arraybuilder::type {}; + + + + enum + { + length = 1 + Tail::length, + }; +}; + +template +struct typelist_cons_v {}; + +template<> +struct typelist_cons_v<> +{ + typedef typelist_end type; +}; + +template +struct typelist_cons_v +{ + typedef typelist::type + > type; +}; + +template +struct typelist_cons {}; + +template +struct typelist_cons +{ + typedef typename typelist_cons_v::type type; +}; + + +#define FGSTD_TYPELIST(x) typename fgstd::typelist_cons::type + +} + + diff --git a/democode/src/demomath/vec.h b/democode/src/demomath/vec.h new file mode 100644 index 0000000..09920d7 --- /dev/null +++ b/democode/src/demomath/vec.h @@ -0,0 +1,219 @@ +#pragma once + +#include +#include +#include "../demolib/demo_def.h" +#include "mathfuncs.h" + +typedef uint8_t vecitype; + +#define TFORALLS(a, b, op) do { for(vecitype i = 0; i < elems; ++i) { (a).ptr()[i] op b; }} while(0) +#define TCLONERETS(b, op) do { V a = asCVR(); TFORALL(a, b, +=); return a; } while(0) +#define TFORALL(a, b, op) TFORALLS(a, (b).cptr()[i], op) +#define TCLONERET(b, op) TCLONERETS((b).cptr()[i], op) + +enum NoInit { noinit }; + +struct tvecbase {}; + +template +struct tvecn : public tvecbase +{ + T e[N]; +}; + +template +struct vecops +{ + typedef T value_type; + enum { elems = N }; + + inline V& operator=(const V& b) { TFORALL(*this, b, =); return asVR(); } + + inline V operator-() const { V a(noinit); for(vecitype i = 0; i < elems; ++i) { a[i] = -(*this)[i]; } return a; } + inline V operator+() const { V a(noinit); for(vecitype i = 0; i < elems; ++i) { a[i] = +(*this)[i]; } return a; } + + inline V operator+(const V& b) const { V a = asCVR(); TFORALL(a, b, +=); return a; } + inline V operator-(const V& b) const { V a = asCVR(); TFORALL(a, b, -=); return a; } + inline V operator*(const V& b) const { V a = asCVR(); TFORALL(a, b, *=); return a; } + inline V operator/(const V& b) const { V a = asCVR(); TFORALL(a, b, /=); return a; } + + inline V operator+(const T& b) const { V a = asCVR(); TFORALLS(a, b, +=); return a; } + inline V operator-(const T& b) const { V a = asCVR(); TFORALLS(a, b, -=); return a; } + inline V operator*(const T& b) const { V a = asCVR(); TFORALLS(a, b, *=); return a; } + inline V operator/(const T& b) const { V a = asCVR(); TFORALLS(a, b, /=); return a; } + + inline V& operator+=(const V& b) { TFORALL(*this, b, +=); return asVR(); } + inline V& operator-=(const V& b) { TFORALL(*this, b, -=); return asVR(); } + inline V& operator*=(const V& b) { TFORALL(*this, b, *=); return asVR(); } + inline V& operator/=(const V& b) { TFORALL(*this, b, /=); return asVR(); } + + inline V& operator+=(const T& b) { TFORALLS(*this, b, +=); return asVR(); } + inline V& operator-=(const T& b) { TFORALLS(*this, b, -=); return asVR(); } + inline V& operator*=(const T& b) { TFORALLS(*this, b, *=); return asVR(); } + inline V& operator/=(const T& b) { TFORALLS(*this, b, /=); return asVR(); } + + + FORCEINLINE const T& operator[](vecitype i) const { return cptr()[i]; } + FORCEINLINE T& operator[](vecitype i) { return ptr()[i]; } + + FORCEINLINE V to(const V& other) const { return other.asCVR() - this->asCVR(); } + + inline bool operator==(const V& o) const + { + for(vecitype i = 0; i < N; ++i) + if((*this)[i] != o[i]) + return false; + return true; + } + FORCEINLINE bool operator!=(const V& o) { return !(*this == o); } + +private: + FORCEINLINE V *asV() { return static_cast(this); } + FORCEINLINE const V *asCV() const { return static_cast(this); } + FORCEINLINE V& asVR() { return *asV(); } + FORCEINLINE const V& asCVR() const { return *asCV(); } + + FORCEINLINE T *ptr() { return reinterpret_cast(asV()); } + FORCEINLINE const T *cptr() const { return reinterpret_cast(asCV()); } +}; + +template +inline typename V::value_type dot(const V& a, const V& b) +{ + typename V::value_type accu(0); + for(vecitype i = 0; i < V::elems; ++i) + accu += (a[i] * b[i]); + return accu; +} + +template +inline typename V::value_type selfdot(const V& a) +{ + typename V::value_type accu(0); + for(vecitype i = 0; i < V::elems; ++i) + { + const typename V::value_type v = a[i]; + accu += v * v; + } + return accu; +} + +template +FORCEINLINE typename V::value_type length(const V& v) +{ + return sqrt(selfdot(v)); +} +template +FORCEINLINE typename V::value_type distance(const V& a, const V& b) +{ + return length(a.to(b)); +} + +template +inline V normalize(const V& v) +{ +#if 0 + typename V::value_type len = length(v); + if(!len) + return V(0); + return v / len; +#else + typename V::value_type invlen = invsqrt(selfdot(v)); + if(!invlen) + return V(0); + return v * invlen; +#endif +} + +template +struct tvec2 : public vecops > +{ + FORCEINLINE tvec2(NoInit) {} + tvec2() { x = T(0); y = T(0); } + explicit tvec2(T p) { x=p; y=p; } + tvec2(T p1, T p2) { x = p1; y = p2; } + tvec2(const tvec2& o) { v = o.v; } + + template + explicit tvec2(const tvec2& o) { x = T(o.x); y = T(o.y); } + + union + { + struct { T x,y; }; + struct { T r,g; }; + tvecn v; + }; + + FORCEINLINE tvec2& operator=(const tvec2& o) { x = o.x; y = o.y; return *this; } +}; + +template +struct tvec3 : public vecops > +{ + FORCEINLINE tvec3(NoInit) {} + tvec3() { x = T(0); y = T(0); z = T(0); } + explicit tvec3(T p) { x=p; y=p; z=p; } + tvec3(T p1, T p2, T p3) { x = p1; y = p2; z = p3; } + tvec3(const tvec3& o) { v = o.v; } + + template + explicit tvec3(const tvec3& o) { x = T(o.x); y = T(o.y); z = T(o.z); } + + union + { + struct { T x,y,z; }; + struct { T r,g,b; }; + struct { T s,t,p; }; + tvecn v; + }; + + FORCEINLINE tvec3& operator=(const tvec3& o) { x = o.x; y = o.y; z = o.z; return *this; } +}; + +template +struct tvec4 : public vecops > +{ + FORCEINLINE tvec4(NoInit) {} + tvec4() { x = T(0); y = T(0); z = T(0); w = T(0); } + explicit tvec4(T p) { x=p; y=p; z=p; w=p; } + tvec4(T p1, T p2, T p3, T p4) { x = p1; y = p2; z = p3; w = p4; } + tvec4(const tvec4& o) { v = o.v; } + //FORCEINLINE tvec4(const tvec3& o) { x = o.x; y = o.y; z = o.z; w = T(1); } + + template + explicit tvec4(const tvec4& o) { x = T(o.x); y = T(o.y); z = T(o.z); w = T(o.w);} + + union + { + struct { T x,y,z,w; }; + struct { T r,g,b,a; }; + struct { T s,t,p,q; }; + tvecn v; + }; + + FORCEINLINE tvec4& operator=(const tvec4& o) { x = o.x; y = o.y; z = o.z; w = o.w; return *this; } +}; + +#undef TFORALL +#undef TCLONERET + + +template +static tvec3 cross(const tvec3& x, const tvec3& y) +{ + return tvec3( + x.y * y.z - y.y * x.z, + x.z * y.x - y.z * x.x, + x.x * y.y - y.x * x.y + ); +} + +// ref must be normalized, then the returned vector and third will also be normalized +template +static tvec3 orthogonalize(const tvec3 &ref, const tvec3 &toOrtho, tvec3 *third) +{ + const tvec3 v = normalize(cross(ref, toOrtho)); + *third = v; + return cross(v, ref); +} diff --git a/democode/src/demomath/view.cpp b/democode/src/demomath/view.cpp new file mode 100644 index 0000000..6fd66d2 --- /dev/null +++ b/democode/src/demomath/view.cpp @@ -0,0 +1,15 @@ +#define NO_SDL + +#include "view.h" + +View::View(const vec3 & origin, const vec3 & at, const vec3 & u) +{ + setOrigin(origin); + setForwardUp(origin.to(at), u); +} + +void View::setForwardUp(const vec3 & fwd, const vec3 & u) +{ + forwardNorm = normalize(fwd); + upNorm = orthogonalize(forwardNorm, u, &rightNorm); // both vectors normalized +} diff --git a/democode/src/demomath/view.h b/democode/src/demomath/view.h new file mode 100644 index 0000000..b34eabd --- /dev/null +++ b/democode/src/demomath/view.h @@ -0,0 +1,29 @@ +#pragma once + +#include "fgmath.h" + +/* A helper class holding information about a point of view and its direction */ +class View +{ +public: + FORCEINLINE View() : origin(noinit), forwardNorm(noinit), upNorm(noinit), rightNorm(noinit) {} + + // This ensures we have a normalized up and right vector, and both are orthogonal to each + // other and to forward. + View(const vec3& origin, const vec3& at, const vec3 &u); + void setForwardUp(const vec3& fwd, const vec3& u); + + FORCEINLINE void setOrigin(const vec3& o) { origin = o; } + + FORCEINLINE const vec3& getOrigin() const { return origin; } + FORCEINLINE const vec3& getForwardNorm() const { return forwardNorm; } + FORCEINLINE const vec3& getUpNorm() const { return upNorm; } + FORCEINLINE const vec3& getRightNorm() const { return rightNorm; } + +protected: + vec3 origin; + // known to be normalized vectors + vec3 forwardNorm; + vec3 upNorm; + vec3 rightNorm; +};