diff --git a/.gitignore b/.gitignore index 1d35ca31..d0568928 100644 --- a/.gitignore +++ b/.gitignore @@ -72,8 +72,12 @@ venv-mnf/ *.lo *.la -# Compilation +# Compilation and Qt preprocessor part +*.qm Makefile +src/qt/komodo-qt +Komodo-Qt.app +background.tiff* # Unit-tests Makefile.test diff --git a/Makefile.am b/Makefile.am index cdce9626..82cbf3e4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,17 +12,27 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libzcashconsensus.pc endif -KOMODOD_BIN=$(top_builddir)/src/zcashd$(EXEEXT) -KOMODO_CLI_BIN=$(top_builddir)/src/zcash-cli$(EXEEXT) -KOMODO_WIN_INSTALLER=$(PACKAGE)-$(PACKAGE_VERSION)-win$(WINDOWS_BITS)-setup$(EXEEXT) +KOMODOD_BIN=$(top_builddir)/src/$(BITCOIN_DAEMON_NAME)$(EXEEXT) +KOMODO_QT_BIN=$(top_builddir)/src/qt/$(BITCOIN_GUI_NAME)$(EXEEXT) +KOMODO_CLI_BIN=$(top_builddir)/src/$(BITCOIN_CLI_NAME)$(EXEEXT) +KOMODO_TX_BIN=$(top_builddir)/src/$(BITCOIN_TX_NAME)$(EXEEXT) +KOMODO_WIN_INSTALLER=$(PACKAGE_NAME)-$(PACKAGE_VERSION)-win$(WINDOWS_BITS)-setup$(EXEEXT) + +empty := +space := $(empty) $(empty) if TARGET_DARWIN OSX_APP=Komodo-Qt.app -OSX_DMG=Komodo-Core.dmg +#OSX_VOLNAME = $(subst $(space),-,$(PACKAGE_NAME)) +OSX_VOLNAME = $(subst $(space),-,$(PACKAGE_NAME)-$(PACKAGE_VERSION)) +OSX_DMG = $(OSX_VOLNAME).dmg +OSX_BACKGROUND_SVG=background.svg OSX_BACKGROUND_IMAGE=background.tiff +OSX_BACKGROUND_IMAGE_DPIS=36 72 +OSX_DSSTORE_GEN=$(top_srcdir)/contrib/macdeploy/custom_dsstore.py OSX_DEPLOY_SCRIPT=$(top_srcdir)/contrib/macdeploy/macdeployqtplus OSX_FANCY_PLIST=$(top_srcdir)/contrib/macdeploy/fancy.plist -OSX_BASE_LPROJ_DIR=$(top_srcdir)/contrib/macdeploy/Base.lproj/InfoPlist.strings +# OSX_BASE_LPROJ_DIR=$(top_srcdir)/contrib/macdeploy/Base.lproj/InfoPlist.strings OSX_INSTALLER_ICONS=$(top_srcdir)/src/qt/res/icons/komodo.icns OSX_PLIST=$(top_srcdir)/share/qt/Info.plist #not installed OSX_QT_TRANSLATIONS = da,de,es,hu,ru,uk,zh_CN,zh_TW @@ -34,15 +44,21 @@ BIN_CHECKS=$(top_srcdir)/contrib/devtools/symbol-check.py \ $(top_srcdir)/contrib/devtools/security-check.py WINDOWS_PACKAGING = $(top_srcdir)/share/pixmaps/komodo.ico \ - $(top_srcdir)/share/pixmaps/nsis-header.bmp \ - $(top_srcdir)/share/pixmaps/nsis-wizard.bmp + $(top_srcdir)/share/pixmaps/nsis-header.bmp \ + $(top_srcdir)/share/pixmaps/nsis-wizard.bmp if TARGET_DARWIN -OSX_PACKAGING = $(OSX_DEPLOY_SCRIPT) $(OSX_FANCY_PLIST) $(OSX_INSTALLER_ICONS) $(OSX_BASE_LPROJ_DIR) \ - $(top_srcdir)/contrib/macdeploy/$(OSX_BACKGROUND_IMAGE) \ - $(top_srcdir)/contrib/macdeploy/DS_Store \ - $(top_srcdir)/contrib/macdeploy/detached-sig-apply.sh \ - $(top_srcdir)/contrib/macdeploy/detached-sig-create.sh + +# OSX_PACKAGING = $(OSX_DEPLOY_SCRIPT) $(OSX_FANCY_PLIST) $(OSX_INSTALLER_ICONS) $(OSX_BASE_LPROJ_DIR) \ +# $(top_srcdir)/contrib/macdeploy/$(OSX_BACKGROUND_IMAGE) \ +# $(top_srcdir)/contrib/macdeploy/DS_Store \ +# $(top_srcdir)/contrib/macdeploy/detached-sig-apply.sh \ +# $(top_srcdir)/contrib/macdeploy/detached-sig-create.sh +OSX_PACKAGING = $(OSX_DEPLOY_SCRIPT) $(OSX_FANCY_PLIST) $(OSX_INSTALLER_ICONS) \ + $(top_srcdir)/contrib/macdeploy/$(OSX_BACKGROUND_SVG) \ + $(OSX_DSSTORE_GEN) \ + $(top_srcdir)/contrib/macdeploy/detached-sig-apply.sh \ + $(top_srcdir)/contrib/macdeploy/detached-sig-create.sh endif if TARGET_DARWIN @@ -60,22 +76,12 @@ COVERAGE_INFO = baseline_filtered_combined.info baseline.info block_test.info \ endif dist-hook: - -$(MAKE) -C $(top_distdir)/src/leveldb clean - -$(MAKE) -C $(top_distdir)/src/secp256k1 distclean -$(GIT) archive --format=tar HEAD -- src/clientversion.cpp | $(AMTAR) -C $(top_distdir) -xf - -distcheck-hook: - $(MKDIR_P) $(top_distdir)/_build/src/leveldb - cp -rf $(top_srcdir)/src/leveldb/* $(top_distdir)/_build/src/leveldb/ - -$(MAKE) -C $(top_distdir)/_build/src/leveldb clean - -distcleancheck: - @: - $(KOMODO_WIN_INSTALLER): all-recursive $(MKDIR_P) $(top_builddir)/release - STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(KOMODOD_BIN) $(top_builddir)/release STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(KOMODO_CLI_BIN) $(top_builddir)/release + STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(KOMODO_QT_BIN) $(top_builddir)/release @test -f $(MAKENSIS) && $(MAKENSIS) -V2 $(top_builddir)/share/setup.nsi || \ echo error: could not build $@ @echo built $@ @@ -101,30 +107,47 @@ $(OSX_APP)/Contents/Resources/komodo.icns: $(OSX_INSTALLER_ICONS) $(MKDIR_P) $(@D) $(INSTALL_DATA) $< $@ +# $(OSX_APP)/Contents/MacOS/Komodo-Qt: $(KOMODO_QT_BIN) +# $(MKDIR_P) $(@D) +# STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $< $@ + $(OSX_APP)/Contents/MacOS/Komodo-Qt: $(KOMODO_QT_BIN) $(MKDIR_P) $(@D) - STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $< $@ + STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $< $@ -$(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings: $(OSX_BASE_LPROJ_DIR) +# $(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings: $(OSX_BASE_LPROJ_DIR) +# $(MKDIR_P) $(@D) +# $(INSTALL_DATA) $< $@ +$(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings: $(MKDIR_P) $(@D) - $(INSTALL_DATA) $< $@ + echo '{ CFBundleDisplayName = "$(PACKAGE_NAME)"; CFBundleName = "$(PACKAGE_NAME)"; }' > $@ OSX_APP_BUILT=$(OSX_APP)/Contents/PkgInfo $(OSX_APP)/Contents/Resources/empty.lproj \ - $(OSX_APP)/Contents/Resources/komodo.icns $(OSX_APP)/Contents/Info.plist \ - $(OSX_APP)/Contents/MacOS/Komodo-Qt $(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings - + $(OSX_APP)/Contents/Resources/komodo.icns $(OSX_APP)/Contents/Info.plist \ + $(OSX_APP)/Contents/MacOS/Komodo-Qt $(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings + endif if BUILD_DARWIN -$(OSX_DMG): $(OSX_APP_BUILT) $(OSX_PACKAGING) - $(OSX_DEPLOY_SCRIPT) $(OSX_APP) -add-qt-tr $(OSX_QT_TRANSLATIONS) -translations-dir=$(QT_TRANSLATION_DIR) -dmg -fancy $(OSX_FANCY_PLIST) -verbose 2 +# $(OSX_DMG): $(OSX_APP_BUILT) $(OSX_PACKAGING) +# $(OSX_DEPLOY_SCRIPT) $(OSX_APP) -add-qt-tr $(OSX_QT_TRANSLATIONS) -translations-dir=$(QT_TRANSLATION_DIR) -dmg -fancy $(OSX_FANCY_PLIST) -verbose 2 +$(OSX_DMG): $(OSX_APP_BUILT) $(OSX_PACKAGING) $(OSX_BACKGROUND_IMAGE) + $(PYTHON) $(OSX_DEPLOY_SCRIPT) $(OSX_APP) -add-qt-tr $(OSX_QT_TRANSLATIONS) -translations-dir=$(QT_TRANSLATION_DIR) -dmg -fancy $(OSX_FANCY_PLIST) -verbose 2 -volname $(OSX_VOLNAME) + +$(OSX_BACKGROUND_IMAGE).png: contrib/macdeploy/$(OSX_BACKGROUND_SVG) + sed 's/PACKAGE_NAME/$(PACKAGE_NAME)/' < "$<" | $(RSVG_CONVERT) -f png -d 36 -p 36 -o $@ +$(OSX_BACKGROUND_IMAGE)@2x.png: contrib/macdeploy/$(OSX_BACKGROUND_SVG) + sed 's/PACKAGE_NAME/$(PACKAGE_NAME)/' < "$<" | $(RSVG_CONVERT) -f png -d 72 -p 72 -o $@ +$(OSX_BACKGROUND_IMAGE): $(OSX_BACKGROUND_IMAGE).png $(OSX_BACKGROUND_IMAGE)@2x.png + tiffutil -cathidpicheck $^ -out $@ + deploydir: $(OSX_DMG) else APP_DIST_DIR=$(top_builddir)/dist APP_DIST_EXTRAS=$(APP_DIST_DIR)/.background/$(OSX_BACKGROUND_IMAGE) $(APP_DIST_DIR)/.DS_Store $(APP_DIST_DIR)/Applications -endif +#endif -if TARGET_DARWIN +#if TARGET_DARWIN $(APP_DIST_DIR)/Applications: @rm -f $@ @cd $(@D); $(LN_S) /Applications $(@F) @@ -132,14 +155,25 @@ $(APP_DIST_DIR)/Applications: $(APP_DIST_EXTRAS): $(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/Komodo-Qt $(OSX_DMG): $(APP_DIST_EXTRAS) - $(GENISOIMAGE) -no-cache-inodes -D -l -probe -V "Komodo-Core" -no-pad -r -apple -o $@ dist - -$(APP_DIST_DIR)/.background/$(OSX_BACKGROUND_IMAGE): contrib/macdeploy/$(OSX_BACKGROUND_IMAGE) + $(GENISOIMAGE) -no-cache-inodes -D -l -probe -V "$(OSX_VOLNAME)" -no-pad -r -dir-mode 0755 -apple -o $@ dist + +# $(APP_DIST_DIR)/.background/$(OSX_BACKGROUND_IMAGE): contrib/macdeploy/$(OSX_BACKGROUND_IMAGE) +# $(MKDIR_P) $(@D) +# $(INSTALL) $< $@ +dpi%.$(OSX_BACKGROUND_IMAGE): contrib/macdeploy/$(OSX_BACKGROUND_SVG) + sed 's/PACKAGE_NAME/$(PACKAGE_NAME)/' < "$<" | $(RSVG_CONVERT) -f png -d $* -p $* | $(IMAGEMAGICK_CONVERT) - $@ +OSX_BACKGROUND_IMAGE_DPIFILES := $(foreach dpi,$(OSX_BACKGROUND_IMAGE_DPIS),dpi$(dpi).$(OSX_BACKGROUND_IMAGE)) +$(APP_DIST_DIR)/.background/$(OSX_BACKGROUND_IMAGE): $(OSX_BACKGROUND_IMAGE_DPIFILES) $(MKDIR_P) $(@D) - $(INSTALL) $< $@ + $(TIFFCP) -c none $(OSX_BACKGROUND_IMAGE_DPIFILES) $@ + $(APP_DIST_DIR)/.DS_Store: contrib/macdeploy/DS_Store + echo $(PYTHON) $< "$@" "$(OSX_VOLNAME)" $(INSTALL) $< $@ +# $(APP_DIST_DIR)/.DS_Store: $(OSX_DSSTORE_GEN) +# $(PYTHON) $< "$@" "$(OSX_VOLNAME)" + $(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/Komodo-Qt: $(OSX_APP_BUILT) $(OSX_PACKAGING) INSTALLNAMETOOL=$(INSTALLNAMETOOL) OTOOL=$(OTOOL) STRIP=$(STRIP) $(OSX_DEPLOY_SCRIPT) $(OSX_APP) -translations-dir=$(QT_TRANSLATION_DIR) -add-qt-tr $(OSX_QT_TRANSLATIONS) -verbose 2 @@ -351,3 +385,4 @@ DISTCHECK_CONFIGURE_FLAGS = --enable-man clean-local: rm -rf test_komodo.coverage/ zcash-gtest.coverage/ total.coverage/ $(OSX_APP) + rm -rf dist/ dpi36.background.tiff dpi72.background.tiff diff --git a/README-dev.md b/README-dev.md index ae817079..4eaf976e 100644 --- a/README-dev.md +++ b/README-dev.md @@ -104,4 +104,15 @@ bcz few procedures uses `vcalc_sha256` even to check incoming block, so it needs ### part 4 -- Probably we have some unexpected / unpredictable behavior (strange side-effect) with this [linux_release_CFLAGS: -O1 -> -O2](https://github.com/DeckerSU/KomodoOcean/commit/511b27aba1c53b05acdc2a169eb374017b7b9145) commit. When wallet built with `CXXFLAGS='-g0 -O2'` all is ok, when with `CXXFLAGS='-g -O2'` - it's ok too. But when we tried to build wallet with just `CXXFLAGS='-g'` in `zcutil/build.sh ` it crashed somewhere in init of [CDBEnv](https://github.com/DeckerSU/KomodoOcean/blob/b8d315bbbece1cb3786855dae40de70a3f8385f0/src/wallet/db.cpp#L49). So, i just want to mention it here. In future it should be investigated to find a solution, may be commit should be reverted or additional BDB build flags should be added. \ No newline at end of file +- Probably we have some unexpected / unpredictable behavior (strange side-effect) with this [linux_release_CFLAGS: -O1 -> -O2](https://github.com/DeckerSU/KomodoOcean/commit/511b27aba1c53b05acdc2a169eb374017b7b9145) commit. When wallet built with `CXXFLAGS='-g0 -O2'` all is ok, when with `CXXFLAGS='-g -O2'` - it's ok too. But when we tried to build wallet with just `CXXFLAGS='-g'` in `zcutil/build.sh ` it crashed somewhere in init of [CDBEnv](https://github.com/DeckerSU/KomodoOcean/blob/b8d315bbbece1cb3786855dae40de70a3f8385f0/src/wallet/db.cpp#L49). So, i just want to mention it here. In future it should be investigated to find a solution, may be commit should be reverted or additional BDB build flags should be added. + +### part 5 + +- If you want to build under Linux for multiple OSes from the same repo / folder, like, build for Win64 and then build for MacOS - you can do the following: +``` +make clean +make -C src/univalue clean +make -C src/cryptoconditions clean +rm src/qt/moc_*.cpp +``` +after each different build. \ No newline at end of file diff --git a/README.md b/README.md index 85169a44..4d885d53 100644 --- a/README.md +++ b/README.md @@ -6,23 +6,13 @@ Komodo-Qt (KomodoOcean) is a world-first Qt native wallet for KMD ([Komodo](https://komodoplatform.com/)) and smartchains (assetchains). It's available for three OS platforms - Windows, Linux, MacOS. -**NB!** Earlier (till 23.05.2019) we had three branches: +Use the default `static` branch and following scripts to build: - -- [master](../../tree/master) for Windows. -- [Linux](../../tree/Linux) for Linux. -- [MacOS](../../tree/MacOS) for MacOS. - -Now we have only one branch [static](../../tree/static) for build static Komodo-Qt binaries from one branch for each OS. - -Use the following scripts to build: - -- Linux: `build-linux.sh` (native build) +- Linux: `build.sh` (native build) - Windows: `build-win.sh` (cross-compilation for Win) +- MacOS: `build-mac-cross.sh` (cross-compilation for OSX) - MacOS: `build-mac.sh` (native build) -`master` branch **can't** be used anymore to build actual wallet version, but it still can be used as an example of build with MSVC compiler. To build actual version plz use `static` branch. - Visit [#wallet-ocean-qt](https://discord.gg/U5WWaJR) channel in Komodo Discord for more information. ## How to build? ## @@ -43,8 +33,18 @@ cd komodo #This can take some time. ``` +#### OSX (Cross-compile) + +Before start, read the following docs: [depends](https://github.com/bitcoin/bitcoin/blob/master/depends/README.md), [macdeploy](https://github.com/bitcoin/bitcoin/blob/master/contrib/macdeploy/README.md) . -#### OSX +Install dependencies: +``` +sudo apt-get install curl librsvg2-bin libtiff-tools bsdmainutils cmake imagemagick libcap-dev libz-dev libbz2-dev python3-setuptools libtinfo5 xorriso +``` + +Place prepared SDK file `Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz` in repo root, use `build-mac-cross.sh` script to build. + +#### OSX (Native) Ensure you have [brew](https://brew.sh) and Command Line Tools installed. ```shell # Install brew @@ -56,7 +56,7 @@ brew update brew upgrade brew tap discoteq/discoteq; brew install flock brew install autoconf autogen automake -brew install gcc@6 +# brew install gcc@6 brew install binutils brew install protobuf brew install coreutils @@ -93,7 +93,7 @@ cd komodo ``` **komodo is experimental and a work-in-progress.** Use at your own risk. - +*p.s.* Currently only `x86_64` arch supported for MacOS, build for `Apple M1` processors unfortunately not yet supported. ## Developers of Qt wallet ## diff --git a/configure.ac b/configure.ac index 5aaa4364..0359d1c1 100644 --- a/configure.ac +++ b/configure.ac @@ -3,11 +3,11 @@ AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 3) define(_CLIENT_VERSION_MINOR, 0) define(_CLIENT_VERSION_REVISION, 1) -define(_CLIENT_VERSION_BUILD, 3) +define(_CLIENT_VERSION_BUILD, 4) define(_ZC_BUILD_VAL, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, m4_incr(_CLIENT_VERSION_BUILD), m4_eval(_CLIENT_VERSION_BUILD < 50), 1, m4_eval(_CLIENT_VERSION_BUILD - 24), m4_eval(_CLIENT_VERSION_BUILD == 50), 1, , m4_eval(_CLIENT_VERSION_BUILD - 50))) define(_CLIENT_VERSION_SUFFIX, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, _CLIENT_VERSION_REVISION-beta$1, m4_eval(_CLIENT_VERSION_BUILD < 50), 1, _CLIENT_VERSION_REVISION-rc$1, m4_eval(_CLIENT_VERSION_BUILD == 50), 1, _CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION-$1))) define(_CLIENT_VERSION_IS_RELEASE, true) -define(_COPYRIGHT_YEAR, 2020) +define(_COPYRIGHT_YEAR, 2021) define(_COPYRIGHT_HOLDERS, "The %s developers") define(_COPYRIGHT_HOLDERS_SUBSTITUTION, "Ocean and Decker") @@ -237,102 +237,102 @@ enable_shani=no if test "x$use_asm" = "xyes"; then -dnl Check for optional instruction set support. Enabling these does _not_ imply that all code will -dnl be compiled with them, rather that specific objects/libs may use them after checking for runtime -dnl compatibility. - -dnl x86 -AX_CHECK_COMPILE_FLAG([-msse4.2],[[SSE42_CXXFLAGS="-msse4.2"]],,[[$CXXFLAG_WERROR]]) -AX_CHECK_COMPILE_FLAG([-msse4.1],[[SSE41_CXXFLAGS="-msse4.1"]],,[[$CXXFLAG_WERROR]]) -AX_CHECK_COMPILE_FLAG([-mavx -mavx2],[[AVX2_CXXFLAGS="-mavx -mavx2"]],,[[$CXXFLAG_WERROR]]) -AX_CHECK_COMPILE_FLAG([-msse4 -msha],[[SHANI_CXXFLAGS="-msse4 -msha"]],,[[$CXXFLAG_WERROR]]) - -TEMP_CXXFLAGS="$CXXFLAGS" -CXXFLAGS="$CXXFLAGS $SSE42_CXXFLAGS" -AC_MSG_CHECKING(for SSE4.2 intrinsics) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - #include - #if defined(_MSC_VER) - #include - #elif defined(__GNUC__) && defined(__SSE4_2__) - #include - #endif - ]],[[ - uint64_t l = 0; - l = _mm_crc32_u8(l, 0); - l = _mm_crc32_u32(l, 0); - l = _mm_crc32_u64(l, 0); - return l; - ]])], - [ AC_MSG_RESULT(yes); enable_sse42=yes], - [ AC_MSG_RESULT(no)] -) -CXXFLAGS="$TEMP_CXXFLAGS" - -TEMP_CXXFLAGS="$CXXFLAGS" -CXXFLAGS="$CXXFLAGS $SSE41_CXXFLAGS" -AC_MSG_CHECKING(for SSE4.1 intrinsics) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - #include - #include - ]],[[ - __m128i l = _mm_set1_epi32(0); - return _mm_extract_epi32(l, 3); - ]])], - [ AC_MSG_RESULT(yes); enable_sse41=yes; AC_DEFINE(ENABLE_SSE41, 1, [Define this symbol to build code that uses SSE4.1 intrinsics]) ], - [ AC_MSG_RESULT(no)] -) -CXXFLAGS="$TEMP_CXXFLAGS" - -TEMP_CXXFLAGS="$CXXFLAGS" -CXXFLAGS="$CXXFLAGS $AVX2_CXXFLAGS" -AC_MSG_CHECKING(for AVX2 intrinsics) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - #include - #include - ]],[[ - __m256i l = _mm256_set1_epi32(0); - return _mm256_extract_epi32(l, 7); - ]])], - [ AC_MSG_RESULT(yes); enable_avx2=yes; AC_DEFINE(ENABLE_AVX2, 1, [Define this symbol to build code that uses AVX2 intrinsics]) ], - [ AC_MSG_RESULT(no)] -) -CXXFLAGS="$TEMP_CXXFLAGS" - -TEMP_CXXFLAGS="$CXXFLAGS" -CXXFLAGS="$CXXFLAGS $SHANI_CXXFLAGS" -AC_MSG_CHECKING(for SHA-NI intrinsics) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - #include - #include - ]],[[ - __m128i i = _mm_set1_epi32(0); - __m128i j = _mm_set1_epi32(1); - __m128i k = _mm_set1_epi32(2); - return _mm_extract_epi32(_mm_sha256rnds2_epu32(i, i, k), 0); - ]])], - [ AC_MSG_RESULT(yes); enable_shani=yes; AC_DEFINE(ENABLE_SHANI, 1, [Define this symbol to build code that uses SHA-NI intrinsics]) ], - [ AC_MSG_RESULT(no)] -) -CXXFLAGS="$TEMP_CXXFLAGS" - -# ARM -AX_CHECK_COMPILE_FLAG([-march=armv8-a+crc+crypto],[[ARM_CRC_CXXFLAGS="-march=armv8-a+crc+crypto"]],,[[$CXXFLAG_WERROR]]) - -TEMP_CXXFLAGS="$CXXFLAGS" -CXXFLAGS="$CXXFLAGS $ARM_CRC_CXXFLAGS" -AC_MSG_CHECKING(for ARM CRC32 intrinsics) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - #include - #include - ]],[[ - __crc32cb(0, 0); __crc32ch(0, 0); __crc32cw(0, 0); __crc32cd(0, 0); - vmull_p64(0, 0); - ]])], - [ AC_MSG_RESULT(yes); enable_arm_crc=yes; ], - [ AC_MSG_RESULT(no)] -) -CXXFLAGS="$TEMP_CXXFLAGS" + dnl Check for optional instruction set support. Enabling these does _not_ imply that all code will + dnl be compiled with them, rather that specific objects/libs may use them after checking for runtime + dnl compatibility. + + dnl x86 + AX_CHECK_COMPILE_FLAG([-msse4.2],[[SSE42_CXXFLAGS="-msse4.2"]],,[[$CXXFLAG_WERROR]]) + AX_CHECK_COMPILE_FLAG([-msse4.1],[[SSE41_CXXFLAGS="-msse4.1"]],,[[$CXXFLAG_WERROR]]) + AX_CHECK_COMPILE_FLAG([-mavx -mavx2],[[AVX2_CXXFLAGS="-mavx -mavx2"]],,[[$CXXFLAG_WERROR]]) + AX_CHECK_COMPILE_FLAG([-msse4 -msha],[[SHANI_CXXFLAGS="-msse4 -msha"]],,[[$CXXFLAG_WERROR]]) + + TEMP_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $SSE42_CXXFLAGS" + AC_MSG_CHECKING(for SSE4.2 intrinsics) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + #if defined(_MSC_VER) + #include + #elif defined(__GNUC__) && defined(__SSE4_2__) + #include + #endif + ]],[[ + uint64_t l = 0; + l = _mm_crc32_u8(l, 0); + l = _mm_crc32_u32(l, 0); + l = _mm_crc32_u64(l, 0); + return l; + ]])], + [ AC_MSG_RESULT(yes); enable_sse42=yes], + [ AC_MSG_RESULT(no)] + ) + CXXFLAGS="$TEMP_CXXFLAGS" + + TEMP_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $SSE41_CXXFLAGS" + AC_MSG_CHECKING(for SSE4.1 intrinsics) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + #include + ]],[[ + __m128i l = _mm_set1_epi32(0); + return _mm_extract_epi32(l, 3); + ]])], + [ AC_MSG_RESULT(yes); enable_sse41=yes; AC_DEFINE(ENABLE_SSE41, 1, [Define this symbol to build code that uses SSE4.1 intrinsics]) ], + [ AC_MSG_RESULT(no)] + ) + CXXFLAGS="$TEMP_CXXFLAGS" + + TEMP_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $AVX2_CXXFLAGS" + AC_MSG_CHECKING(for AVX2 intrinsics) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + #include + ]],[[ + __m256i l = _mm256_set1_epi32(0); + return _mm256_extract_epi32(l, 7); + ]])], + [ AC_MSG_RESULT(yes); enable_avx2=yes; AC_DEFINE(ENABLE_AVX2, 1, [Define this symbol to build code that uses AVX2 intrinsics]) ], + [ AC_MSG_RESULT(no)] + ) + CXXFLAGS="$TEMP_CXXFLAGS" + + TEMP_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $SHANI_CXXFLAGS" + AC_MSG_CHECKING(for SHA-NI intrinsics) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + #include + ]],[[ + __m128i i = _mm_set1_epi32(0); + __m128i j = _mm_set1_epi32(1); + __m128i k = _mm_set1_epi32(2); + return _mm_extract_epi32(_mm_sha256rnds2_epu32(i, i, k), 0); + ]])], + [ AC_MSG_RESULT(yes); enable_shani=yes; AC_DEFINE(ENABLE_SHANI, 1, [Define this symbol to build code that uses SHA-NI intrinsics]) ], + [ AC_MSG_RESULT(no)] + ) + CXXFLAGS="$TEMP_CXXFLAGS" + + # ARM + AX_CHECK_COMPILE_FLAG([-march=armv8-a+crc+crypto],[[ARM_CRC_CXXFLAGS="-march=armv8-a+crc+crypto"]],,[[$CXXFLAG_WERROR]]) + + TEMP_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $ARM_CRC_CXXFLAGS" + AC_MSG_CHECKING(for ARM CRC32 intrinsics) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + #include + ]],[[ + __crc32cb(0, 0); __crc32ch(0, 0); __crc32cw(0, 0); __crc32cd(0, 0); + vmull_p64(0, 0); + ]])], + [ AC_MSG_RESULT(yes); enable_arm_crc=yes; ], + [ AC_MSG_RESULT(no)] + ) + CXXFLAGS="$TEMP_CXXFLAGS" fi @@ -401,7 +401,7 @@ case $host in fi CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB" - LEVELDB_TARGET_FLAGS="TARGET_OS=OS_WINDOWS_CROSSCOMPILE" + LEVELDB_TARGET_FLAGS="-DOS_WINDOWS" if test "x$CXXFLAGS_overridden" = "xno"; then CXXFLAGS="$CXXFLAGS -w" fi @@ -423,7 +423,7 @@ case $host in ;; *darwin*) TARGET_OS=darwin - LEVELDB_TARGET_FLAGS="TARGET_OS=Darwin" + LEVELDB_TARGET_FLAGS="-DOS_MACOSX" if test x$cross_compiling != xyes; then BUILD_OS=darwin AC_CHECK_PROG([PORT],port, port) @@ -489,8 +489,11 @@ case $host in ;; *linux*) TARGET_OS=linux + LEVELDB_TARGET_FLAGS="-DOS_LINUX" ;; *) + OTHER_OS=`echo ${host_os} | awk '{print toupper($0)}'` + LEVELDB_TARGET_FLAGS="-DOS_${OTHER_OS}" ;; esac @@ -604,8 +607,8 @@ if test x$use_hardening != xno; then if test x$TARGET_OS != xwindows; then # All windows code is PIC, forcing it on just adds useless compile warnings - AX_CHECK_LINK_FLAG([[-Wl,-z,relro]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,relro"],[AC_MSG_ERROR(Cannot enable RELRO)]) - AX_CHECK_LINK_FLAG([[-Wl,-z,now]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,now"],[AC_MSG_ERROR(Cannot enable BIND_NOW)]) + # AX_CHECK_LINK_FLAG([[-Wl,-z,relro]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,relro"],[AC_MSG_ERROR(Cannot enable RELRO)]) + # AX_CHECK_LINK_FLAG([[-Wl,-z,now]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,now"],[AC_MSG_ERROR(Cannot enable BIND_NOW)]) AX_CHECK_COMPILE_FLAG([-fPIE],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fPIE"],[AC_MSG_ERROR(Cannot enable -fPIE)]) AX_CHECK_LINK_FLAG([[-pie]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -pie"],[AC_MSG_ERROR(Cannot enable -pie)]) else @@ -956,7 +959,7 @@ CPPFLAGS="-I$LIBSNARK_INCDIR $CPPFLAGS" # Now check for libsnark compilability using traditional autoconf tests: if test x$TARGET_OS = xdarwin; then AC_CHECK_HEADER([libsnark/gadgetlib1/gadget.hpp],,AC_MSG_ERROR(libsnark headers missing)) -AC_CHECK_LIB([snark],[main],LIBSNARK_LIBS=-lsnark, [AC_MSG_ERROR(libsnark missing)], [-lgmpxx]) +#AC_CHECK_LIB([snark],[main],LIBSNARK_LIBS=-lsnark, [AC_MSG_ERROR(libsnark missing)], [-lgmpxx]) fi #AC_CHECK_HEADER([libsnark/gadgetlib1/gadget.hpp],,AC_MSG_ERROR(libsnark headers missing)) #AC_CHECK_LIB([snark],[main],LIBSNARK_LIBS=-lsnark, [AC_MSG_ERROR(libsnark missing)], [-lgmpxx]) @@ -1201,6 +1204,11 @@ AC_SUBST(COPYRIGHT_YEAR, _COPYRIGHT_YEAR) AC_SUBST(COPYRIGHT_HOLDERS, _COPYRIGHT_HOLDERS) AC_SUBST(COPYRIGHT_HOLDERS_SUBSTITUTION, _COPYRIGHT_HOLDERS_SUBSTITUTION) +AC_SUBST(BITCOIN_DAEMON_NAME) +AC_SUBST(BITCOIN_GUI_NAME) +AC_SUBST(BITCOIN_CLI_NAME) +AC_SUBST(BITCOIN_TX_NAME) + AC_SUBST(RELDFLAGS) AC_SUBST(SSE42_CXXFLAGS) AC_SUBST(SSE41_CXXFLAGS) @@ -1212,13 +1220,15 @@ AC_SUBST(USE_UPNP) AC_SUBST(BOOST_LIBS) AC_SUBST(TESTDEFS) AC_SUBST(LEVELDB_TARGET_FLAGS) +AC_SUBST(LEVELDB_ATOMIC_CPPFLAGS) +AC_SUBST(LEVELDB_ATOMIC_CXXFLAGS) AC_SUBST(MINIUPNPC_CPPFLAGS) AC_SUBST(MINIUPNPC_LIBS) AC_SUBST(GMP_LIBS) AC_SUBST(GMPXX_LIBS) AC_SUBST(LIBSNARK_DEPINST) AC_SUBST(LIBZCASH_LIBS) -AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi src/test/buildenv.py]) +AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist src/test/buildenv.py]) AC_CONFIG_FILES([qa/pull-tester/run-komodod-for-test.sh],[chmod +x qa/pull-tester/run-komodod-for-test.sh]) AC_CONFIG_FILES([qa/pull-tester/tests-config.sh],[chmod +x qa/pull-tester/tests-config.sh]) diff --git a/contrib/macdeploy/DS_Store b/contrib/macdeploy/DS_Store new file mode 100644 index 00000000..8c7f77dc Binary files /dev/null and b/contrib/macdeploy/DS_Store differ diff --git a/contrib/macdeploy/LICENSE b/contrib/macdeploy/LICENSE new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/contrib/macdeploy/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/contrib/macdeploy/README.md b/contrib/macdeploy/README.md new file mode 100644 index 00000000..9aad70b0 --- /dev/null +++ b/contrib/macdeploy/README.md @@ -0,0 +1,128 @@ +# MacOS Deployment + +The `macdeployqtplus` script should not be run manually. Instead, after building as usual: + +```bash +make deploy +``` + +During the deployment process, the disk image window will pop up briefly +when the fancy settings are applied. This is normal, please do not interfere, +the process will unmount the DMG and cleanup before finishing. + +When complete, it will have produced `Komodo-Qt.dmg`. + +## SDK Extraction + +### Step 1: Obtaining `Xcode.app` + +Our current macOS SDK +(`Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz`) can be +extracted from +[Xcode_11.3.1.xip](https://download.developer.apple.com/Developer_Tools/Xcode_11.3.1/Xcode_11.3.1.xip). +An Apple ID is needed to download this. + +After Xcode version 7.x, Apple started shipping the `Xcode.app` in a `.xip` +archive. This makes the SDK less-trivial to extract on non-macOS machines. One +approach (tested on Debian Buster) is outlined below: + +```bash +# Install/clone tools needed for extracting Xcode.app +apt install cpio +git clone https://github.com/bitcoin-core/apple-sdk-tools.git + +# Unpack Xcode_11.3.1.xip and place the resulting Xcode.app in your current +# working directory +python3 apple-sdk-tools/extract_xcode.py -f Xcode_11.3.1.xip | cpio -d -i +``` + +On macOS the process is more straightforward: + +```bash +xip -x Xcode_11.3.1.xip +``` + +### Step 2: Generating `Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz` from `Xcode.app` + +To generate `Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz`, run +the script [`gen-sdk`](./gen-sdk) with the path to `Xcode.app` (extracted in the +previous stage) as the first argument. + +```bash +# Generate a Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz from +# the supplied Xcode.app +./contrib/macdeploy/gen-sdk '/path/to/Xcode.app' +``` + +## Deterministic macOS DMG Notes +Working macOS DMGs are created in Linux by combining a recent `clang`, the Apple +`binutils` (`ld`, `ar`, etc) and DMG authoring tools. + +Apple uses `clang` extensively for development and has upstreamed the necessary +functionality so that a vanilla clang can take advantage. It supports the use of `-F`, +`-target`, `-mmacosx-version-min`, and `--sysroot`, which are all necessary when +building for macOS. + +Apple's version of `binutils` (called `cctools`) contains lots of functionality missing in the +FSF's `binutils`. In addition to extra linker options for frameworks and sysroots, several +other tools are needed as well such as `install_name_tool`, `lipo`, and `nmedit`. These +do not build under Linux, so they have been patched to do so. The work here was used as +a starting point: [mingwandroid/toolchain4](https://github.com/mingwandroid/toolchain4). + +In order to build a working toolchain, the following source packages are needed from +Apple: `cctools`, `dyld`, and `ld64`. + +These tools inject timestamps by default, which produce non-deterministic binaries. The +`ZERO_AR_DATE` environment variable is used to disable that. + +This version of `cctools` has been patched to use the current version of `clang`'s headers +and its `libLTO.so` rather than those from `llvmgcc`, as it was originally done in `toolchain4`. + +To complicate things further, all builds must target an Apple SDK. These SDKs are free to +download, but not redistributable. To obtain it, register for an Apple Developer Account, +then download [Xcode_11.3.1](https://download.developer.apple.com/Developer_Tools/Xcode_11.3.1/Xcode_11.3.1.xip). + +This file is many gigabytes in size, but most (but not all) of what we need is +contained only in a single directory: + +```bash +Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk +``` + +See the SDK Extraction notes above for how to obtain it. + +The Gitian descriptors build 2 sets of files: Linux tools, then Apple binaries which are +created using these tools. The build process has been designed to avoid including the +SDK's files in Gitian's outputs. All interim tarballs are fully deterministic and may be freely +redistributed. + +`genisoimage` is used to create the initial DMG. It is not deterministic as-is, so it has been +patched. A system `genisoimage` will work fine, but it will not be deterministic because +the file-order will change between invocations. The patch can be seen here: [cdrkit-deterministic.patch](https://github.com/bitcoin/bitcoin/blob/master/depends/patches/native_cdrkit/cdrkit-deterministic.patch). +No effort was made to fix this cleanly, so it likely leaks memory badly, however it's only used for +a single invocation, so that's no real concern. + +`genisoimage` cannot compress DMGs, so afterwards, the DMG tool from the +`libdmg-hfsplus` project is used to compress it. There are several bugs in this tool and its +maintainer has seemingly abandoned the project. + +The DMG tool has the ability to create DMGs from scratch as well, but this functionality is +broken. Only the compression feature is currently used. Ideally, the creation could be fixed +and `genisoimage` would no longer be necessary. + +Background images and other features can be added to DMG files by inserting a +`.DS_Store` before creation. This is generated by the script `contrib/macdeploy/custom_dsstore.py`. + +As of OS X 10.9 Mavericks, using an Apple-blessed key to sign binaries is a requirement in +order to satisfy the new Gatekeeper requirements. Because this private key cannot be +shared, we'll have to be a bit creative in order for the build process to remain somewhat +deterministic. Here's how it works: + +- Builders use Gitian to create an unsigned release. This outputs an unsigned DMG which + users may choose to bless and run. It also outputs an unsigned app structure in the form + of a tarball, which also contains all of the tools that have been previously (deterministically) + built in order to create a final DMG. +- The Apple keyholder uses this unsigned app to create a detached signature, using the + script that is also included there. Detached signatures are available from this [repository](https://github.com/bitcoin-core/bitcoin-detached-sigs). +- Builders feed the unsigned app + detached signature back into Gitian. It uses the + pre-built tools to recombine the pieces into a deterministic DMG. diff --git a/contrib/macdeploy/background.svg b/contrib/macdeploy/background.svg new file mode 100644 index 00000000..9c330af4 --- /dev/null +++ b/contrib/macdeploy/background.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + PACKAGE_NAME + + + + + diff --git a/contrib/macdeploy/custom_dsstore.py b/contrib/macdeploy/custom_dsstore.py new file mode 100755 index 00000000..02ed7b49 --- /dev/null +++ b/contrib/macdeploy/custom_dsstore.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# Copyright (c) 2013-2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +import biplist +from ds_store import DSStore +from mac_alias import Alias +import sys + +output_file = sys.argv[1] +package_name_ns = sys.argv[2] + +ds = DSStore.open(output_file, 'w+') +ds['.']['bwsp'] = { + 'ShowStatusBar': False, + 'WindowBounds': '{{300, 280}, {500, 343}}', + 'ContainerShowSidebar': False, + 'SidebarWidth': 0, + 'ShowTabView': False, + 'PreviewPaneVisibility': False, + 'ShowToolbar': False, + 'ShowSidebar': False, + 'ShowPathbar': True +} + +icvp = { + 'gridOffsetX': 0.0, + 'textSize': 12.0, + 'viewOptionsVersion': 1, + 'backgroundImageAlias': b'\x00\x00\x00\x00\x02\x1e\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd1\x94\\\xb0H+\x00\x05\x00\x00\x00\x98\x0fbackground.tiff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x99\xd19\xb0\xf8\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\r\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b.background\x00\x00\x10\x00\x08\x00\x00\xd1\x94\\\xb0\x00\x00\x00\x11\x00\x08\x00\x00\xd19\xb0\xf8\x00\x00\x00\x01\x00\x04\x00\x00\x00\x98\x00\x0e\x00 \x00\x0f\x00b\x00a\x00c\x00k\x00g\x00r\x00o\x00u\x00n\x00d\x00.\x00t\x00i\x00f\x00f\x00\x0f\x00\x02\x00\x00\x00\x12\x00\x1c/.background/background.tiff\x00\x14\x01\x06\x00\x00\x00\x00\x01\x06\x00\x02\x00\x00\x0cMacintosh HD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xce\x97\xab\xc3H+\x00\x00\x01\x88[\x88\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02u\xab\x8d\xd1\x94\\\xb0devrddsk\xff\xff\xff\xff\x00\x00\t \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07bitcoin\x00\x00\x10\x00\x08\x00\x00\xce\x97\xab\xc3\x00\x00\x00\x11\x00\x08\x00\x00\xd1\x94\\\xb0\x00\x00\x00\x01\x00\x14\x01\x88[\x88\x00\x16\xa9\t\x00\x08\xfaR\x00\x08\xfaQ\x00\x02d\x8e\x00\x0e\x00\x02\x00\x00\x00\x0f\x00\x1a\x00\x0c\x00M\x00a\x00c\x00i\x00n\x00t\x00o\x00s\x00h\x00 \x00H\x00D\x00\x13\x00\x01/\x00\x00\x15\x00\x02\x00\x14\xff\xff\x00\x00\xff\xff\x00\x00', + 'backgroundColorBlue': 1.0, + 'iconSize': 96.0, + 'backgroundColorGreen': 1.0, + 'arrangeBy': 'none', + 'showIconPreview': True, + 'gridSpacing': 100.0, + 'gridOffsetY': 0.0, + 'showItemInfo': False, + 'labelOnBottom': True, + 'backgroundType': 2, + 'backgroundColorRed': 1.0 +} +alias = Alias.from_bytes(icvp['backgroundImageAlias']) +alias.volume.name = package_name_ns +alias.volume.posix_path = '/Volumes/' + package_name_ns +alias.volume.disk_image_alias.target.filename = package_name_ns + '.temp.dmg' +alias.volume.disk_image_alias.target.carbon_path = 'Macintosh HD:Users:\x00komodouser:\x00Documents:\x00komodo:\x00komodo:\x00' + package_name_ns + '.temp.dmg' +alias.volume.disk_image_alias.target.posix_path = 'Users/komodouser/Documents/komodo/komodo/' + package_name_ns + '.temp.dmg' +alias.target.carbon_path = package_name_ns + ':.background:\x00background.tiff' +icvp['backgroundImageAlias'] = biplist.Data(alias.to_bytes()) +ds['.']['icvp'] = icvp + +ds['.']['vSrn'] = ('long', 1) + +ds['Applications']['Iloc'] = (370, 156) +ds['Komodo-Qt.app']['Iloc'] = (128, 156) + +ds.flush() +ds.close() diff --git a/contrib/macdeploy/detached-sig-apply.sh b/contrib/macdeploy/detached-sig-apply.sh new file mode 100755 index 00000000..5c5a85d3 --- /dev/null +++ b/contrib/macdeploy/detached-sig-apply.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# Copyright (c) 2014-2019 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +export LC_ALL=C +set -e + +UNSIGNED="$1" +SIGNATURE="$2" +ARCH=x86_64 +ROOTDIR=dist +TEMPDIR=signed.temp +OUTDIR=signed-app + +if [ -z "$UNSIGNED" ]; then + echo "usage: $0 " + exit 1 +fi + +if [ -z "$SIGNATURE" ]; then + echo "usage: $0 " + exit 1 +fi + +rm -rf ${TEMPDIR} && mkdir -p ${TEMPDIR} +tar -C ${TEMPDIR} -xf ${UNSIGNED} +cp -rf "${SIGNATURE}"/* ${TEMPDIR} + +if [ -z "${PAGESTUFF}" ]; then + PAGESTUFF=${TEMPDIR}/pagestuff +fi + +if [ -z "${CODESIGN_ALLOCATE}" ]; then + CODESIGN_ALLOCATE=${TEMPDIR}/codesign_allocate +fi + +find ${TEMPDIR} -name "*.sign" | while read i; do + SIZE=$(stat -c %s "${i}") + TARGET_FILE="$(echo "${i}" | sed 's/\.sign$//')" + + echo "Allocating space for the signature of size ${SIZE} in ${TARGET_FILE}" + ${CODESIGN_ALLOCATE} -i "${TARGET_FILE}" -a ${ARCH} ${SIZE} -o "${i}.tmp" + + OFFSET=$(${PAGESTUFF} "${i}.tmp" -p | tail -2 | grep offset | sed 's/[^0-9]*//g') + if [ -z ${QUIET} ]; then + echo "Attaching signature at offset ${OFFSET}" + fi + + dd if="$i" of="${i}.tmp" bs=1 seek=${OFFSET} count=${SIZE} 2>/dev/null + mv "${i}.tmp" "${TARGET_FILE}" + rm "${i}" + echo "Success." +done +mv ${TEMPDIR}/${ROOTDIR} ${OUTDIR} +rm -rf ${TEMPDIR} +echo "Signed: ${OUTDIR}" diff --git a/contrib/macdeploy/detached-sig-create.sh b/contrib/macdeploy/detached-sig-create.sh new file mode 100755 index 00000000..91c06d7a --- /dev/null +++ b/contrib/macdeploy/detached-sig-create.sh @@ -0,0 +1,52 @@ +#!/bin/sh +# Copyright (c) 2014-2019 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +export LC_ALL=C +set -e + +ROOTDIR=dist +BUNDLE="${ROOTDIR}/Komodo-Qt.app" +CODESIGN=codesign +TEMPDIR=sign.temp +TEMPLIST=${TEMPDIR}/signatures.txt +OUT=signature-osx.tar.gz +OUTROOT=osx + +if [ -z "$1" ]; then + echo "usage: $0 " + echo "example: $0 -s MyIdentity" + exit 1 +fi + +rm -rf ${TEMPDIR} ${TEMPLIST} +mkdir -p ${TEMPDIR} + +${CODESIGN} -f --file-list ${TEMPLIST} "$@" "${BUNDLE}" + +grep -v CodeResources < "${TEMPLIST}" | while read i; do + TARGETFILE="${BUNDLE}/$(echo "${i}" | sed "s|.*${BUNDLE}/||")" + SIZE=$(pagestuff "$i" -p | tail -2 | grep size | sed 's/[^0-9]*//g') + OFFSET=$(pagestuff "$i" -p | tail -2 | grep offset | sed 's/[^0-9]*//g') + SIGNFILE="${TEMPDIR}/${OUTROOT}/${TARGETFILE}.sign" + DIRNAME="$(dirname "${SIGNFILE}")" + mkdir -p "${DIRNAME}" + echo "Adding detached signature for: ${TARGETFILE}. Size: ${SIZE}. Offset: ${OFFSET}" + dd if="$i" of="${SIGNFILE}" bs=1 skip=${OFFSET} count=${SIZE} 2>/dev/null +done + +grep CodeResources < "${TEMPLIST}" | while read i; do + TARGETFILE="${BUNDLE}/$(echo "${i}" | sed "s|.*${BUNDLE}/||")" + RESOURCE="${TEMPDIR}/${OUTROOT}/${TARGETFILE}" + DIRNAME="$(dirname "${RESOURCE}")" + mkdir -p "${DIRNAME}" + echo "Adding resource for: \"${TARGETFILE}\"" + cp "${i}" "${RESOURCE}" +done + +rm ${TEMPLIST} + +tar -C "${TEMPDIR}" -czf "${OUT}" . +rm -rf "${TEMPDIR}" +echo "Created ${OUT}" diff --git a/contrib/macdeploy/extract-osx-sdk.sh b/contrib/macdeploy/extract-osx-sdk.sh new file mode 100755 index 00000000..3c7bdf42 --- /dev/null +++ b/contrib/macdeploy/extract-osx-sdk.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Copyright (c) 2016-2020 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +export LC_ALL=C +set -e + +INPUTFILE="Xcode_7.3.1.dmg" +HFSFILENAME="5.hfs" +SDKDIR="Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk" + +7z x "${INPUTFILE}" "${HFSFILENAME}" +SDKNAME="$(basename "${SDKDIR}")" +SDKDIRINODE=$(ifind -n "${SDKDIR}" "${HFSFILENAME}") +fls "${HFSFILENAME}" -rpF ${SDKDIRINODE} | + while read type inode filename; do + inode="${inode::-1}" + if [ "${filename:0:14}" = "usr/share/man/" ]; then + continue + fi + filename="${SDKNAME}/$filename" + echo "Extracting $filename ..." + mkdir -p "$(dirname "$filename")" + if [ "$type" = "l/l" ]; then + ln -s "$(icat "${HFSFILENAME}" $inode)" "$filename" + else + icat "${HFSFILENAME}" $inode >"$filename" + fi +done +echo "Building ${SDKNAME}.tar.gz ..." +MTIME="$(istat "${HFSFILENAME}" "${SDKDIRINODE}" | perl -nle 'm/Content Modified:\s+(.*?)\s\(/ && print $1')" +find "${SDKNAME}" | sort | tar --no-recursion --mtime="${MTIME}" --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > "${SDKNAME}.tar.gz" +echo 'All done!' diff --git a/contrib/macdeploy/fancy.plist b/contrib/macdeploy/fancy.plist new file mode 100644 index 00000000..02778971 --- /dev/null +++ b/contrib/macdeploy/fancy.plist @@ -0,0 +1,32 @@ + + + + + window_bounds + + 300 + 300 + 800 + 620 + + background_picture + background.tiff + icon_size + 96 + applications_symlink + + items_position + + Applications + + 370 + 156 + + Komodo-Qt.app + + 128 + 156 + + + + diff --git a/contrib/macdeploy/gen-sdk b/contrib/macdeploy/gen-sdk new file mode 100755 index 00000000..457d8f5e --- /dev/null +++ b/contrib/macdeploy/gen-sdk @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +import argparse +import plistlib +import pathlib +import sys +import tarfile +import gzip +import os +import contextlib + +@contextlib.contextmanager +def cd(path): + """Context manager that restores PWD even if an exception was raised.""" + old_pwd = os.getcwd() + os.chdir(str(path)) + try: + yield + finally: + os.chdir(old_pwd) + +def run(): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + + parser.add_argument('xcode_app', metavar='XCODEAPP', nargs=1) + parser.add_argument("-o", metavar='OUTSDKTGZ', nargs=1, dest='out_sdktgz', required=False) + + args = parser.parse_args() + + xcode_app = pathlib.Path(args.xcode_app[0]).resolve() + assert xcode_app.is_dir(), "The supplied Xcode.app path '{}' either does not exist or is not a directory".format(xcode_app) + + xcode_app_plist = xcode_app.joinpath("Contents/version.plist") + with xcode_app_plist.open('rb') as fp: + pl = plistlib.load(fp) + xcode_version = pl['CFBundleShortVersionString'] + xcode_build_id = pl['ProductBuildVersion'] + print("Found Xcode (version: {xcode_version}, build id: {xcode_build_id})".format(xcode_version=xcode_version, xcode_build_id=xcode_build_id)) + + sdk_dir = xcode_app.joinpath("Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk") + sdk_plist = sdk_dir.joinpath("System/Library/CoreServices/SystemVersion.plist") + with sdk_plist.open('rb') as fp: + pl = plistlib.load(fp) + sdk_version = pl['ProductVersion'] + sdk_build_id = pl['ProductBuildVersion'] + print("Found MacOSX SDK (version: {sdk_version}, build id: {sdk_build_id})".format(sdk_version=sdk_version, sdk_build_id=sdk_build_id)) + + out_name = "Xcode-{xcode_version}-{xcode_build_id}-extracted-SDK-with-libcxx-headers".format(xcode_version=xcode_version, xcode_build_id=xcode_build_id) + + xcode_libcxx_dir = xcode_app.joinpath("Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1") + assert xcode_libcxx_dir.is_dir() + + if args.out_sdktgz: + out_sdktgz_path = pathlib.Path(args.out_sdktgz_path) + else: + # Construct our own out_sdktgz if not specified on the command line + out_sdktgz_path = pathlib.Path("./{}.tar.gz".format(out_name)) + + def tarfp_add_with_base_change(tarfp, dir_to_add, alt_base_dir): + """Add all files in dir_to_add to tarfp, but prepent MEMBERPREFIX to the files' + names + + e.g. if the only file under /root/bazdir is /root/bazdir/qux, invoking: + + tarfp_add_with_base_change(tarfp, "foo/bar", "/root/bazdir") + + would result in the following members being added to tarfp: + + foo/bar/ -> corresponding to /root/bazdir + foo/bar/qux -> corresponding to /root/bazdir/qux + + """ + def change_tarinfo_base(tarinfo): + if tarinfo.name and tarinfo.name.startswith("./"): + tarinfo.name = str(pathlib.Path(alt_base_dir, tarinfo.name)) + if tarinfo.linkname and tarinfo.linkname.startswith("./"): + tarinfo.linkname = str(pathlib.Path(alt_base_dir, tarinfo.linkname)) + return tarinfo + with cd(dir_to_add): + tarfp.add(".", recursive=True, filter=change_tarinfo_base) + + print("Creating output .tar.gz file...") + with out_sdktgz_path.open("wb") as fp: + with gzip.GzipFile(fileobj=fp, compresslevel=9, mtime=0) as gzf: + with tarfile.open(mode="w", fileobj=gzf) as tarfp: + print("Adding MacOSX SDK {} files...".format(sdk_version)) + tarfp_add_with_base_change(tarfp, sdk_dir, out_name) + print("Adding libc++ headers...") + tarfp_add_with_base_change(tarfp, xcode_libcxx_dir, "{}/usr/include/c++/v1".format(out_name)) + print("Done! Find the resulting gzipped tarball at:") + print(out_sdktgz_path.resolve()) + +if __name__ == '__main__': + run() diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus new file mode 100755 index 00000000..08168f00 --- /dev/null +++ b/contrib/macdeploy/macdeployqtplus @@ -0,0 +1,908 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2011 Patrick "p2k" Schneider +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import subprocess, sys, re, os, shutil, stat, os.path, time +from string import Template +from argparse import ArgumentParser +from typing import List, Optional + +# This is ported from the original macdeployqt with modifications + +class FrameworkInfo(object): + def __init__(self): + self.frameworkDirectory = "" + self.frameworkName = "" + self.frameworkPath = "" + self.binaryDirectory = "" + self.binaryName = "" + self.binaryPath = "" + self.version = "" + self.installName = "" + self.deployedInstallName = "" + self.sourceFilePath = "" + self.destinationDirectory = "" + self.sourceResourcesDirectory = "" + self.sourceVersionContentsDirectory = "" + self.sourceContentsDirectory = "" + self.destinationResourcesDirectory = "" + self.destinationVersionContentsDirectory = "" + + def __eq__(self, other): + if self.__class__ == other.__class__: + return self.__dict__ == other.__dict__ + else: + return False + + def __str__(self): + return """ Framework name: {} + Framework directory: {} + Framework path: {} + Binary name: {} + Binary directory: {} + Binary path: {} + Version: {} + Install name: {} + Deployed install name: {} + Source file Path: {} + Deployed Directory (relative to bundle): {} +""".format(self.frameworkName, + self.frameworkDirectory, + self.frameworkPath, + self.binaryName, + self.binaryDirectory, + self.binaryPath, + self.version, + self.installName, + self.deployedInstallName, + self.sourceFilePath, + self.destinationDirectory) + + def isDylib(self): + return self.frameworkName.endswith(".dylib") + + def isQtFramework(self): + if self.isDylib(): + return self.frameworkName.startswith("libQt") + else: + return self.frameworkName.startswith("Qt") + + reOLine = re.compile(r'^(.+) \(compatibility version [0-9.]+, current version [0-9.]+\)$') + bundleFrameworkDirectory = "Contents/Frameworks" + bundleBinaryDirectory = "Contents/MacOS" + + @classmethod + def fromOtoolLibraryLine(cls, line: str) -> Optional['FrameworkInfo']: + # Note: line must be trimmed + if line == "": + return None + + # Don't deploy system libraries (exception for libQtuitools and libQtlucene). + if line.startswith("/System/Library/") or line.startswith("@executable_path") or (line.startswith("/usr/lib/") and "libQt" not in line): + return None + + m = cls.reOLine.match(line) + if m is None: + raise RuntimeError("otool line could not be parsed: " + line) + + path = m.group(1) + + info = cls() + info.sourceFilePath = path + info.installName = path + + if path.endswith(".dylib"): + dirname, filename = os.path.split(path) + info.frameworkName = filename + info.frameworkDirectory = dirname + info.frameworkPath = path + + info.binaryDirectory = dirname + info.binaryName = filename + info.binaryPath = path + info.version = "-" + + info.installName = path + info.deployedInstallName = "@executable_path/../Frameworks/" + info.binaryName + info.sourceFilePath = path + info.destinationDirectory = cls.bundleFrameworkDirectory + else: + parts = path.split("/") + i = 0 + # Search for the .framework directory + for part in parts: + if part.endswith(".framework"): + break + i += 1 + if i == len(parts): + raise RuntimeError("Could not find .framework or .dylib in otool line: " + line) + + info.frameworkName = parts[i] + info.frameworkDirectory = "/".join(parts[:i]) + info.frameworkPath = os.path.join(info.frameworkDirectory, info.frameworkName) + + info.binaryName = parts[i+3] + info.binaryDirectory = "/".join(parts[i+1:i+3]) + info.binaryPath = os.path.join(info.binaryDirectory, info.binaryName) + info.version = parts[i+2] + + info.deployedInstallName = "@executable_path/../Frameworks/" + os.path.join(info.frameworkName, info.binaryPath) + info.destinationDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, info.binaryDirectory) + + info.sourceResourcesDirectory = os.path.join(info.frameworkPath, "Resources") + info.sourceContentsDirectory = os.path.join(info.frameworkPath, "Contents") + info.sourceVersionContentsDirectory = os.path.join(info.frameworkPath, "Versions", info.version, "Contents") + info.destinationResourcesDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Resources") + info.destinationVersionContentsDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Versions", info.version, "Contents") + + return info + +class ApplicationBundleInfo(object): + def __init__(self, path: str): + self.path = path + appName = "Komodo-Qt" + self.binaryPath = os.path.join(path, "Contents", "MacOS", appName) + if not os.path.exists(self.binaryPath): + raise RuntimeError("Could not find bundle binary for " + path) + self.resourcesPath = os.path.join(path, "Contents", "Resources") + self.pluginPath = os.path.join(path, "Contents", "PlugIns") + +class DeploymentInfo(object): + def __init__(self): + self.qtPath = None + self.pluginPath = None + self.deployedFrameworks = [] + + def detectQtPath(self, frameworkDirectory: str): + parentDir = os.path.dirname(frameworkDirectory) + if os.path.exists(os.path.join(parentDir, "translations")): + # Classic layout, e.g. "/usr/local/Trolltech/Qt-4.x.x" + self.qtPath = parentDir + else: + self.qtPath = os.getenv("QTDIR", None) + + if self.qtPath is not None: + pluginPath = os.path.join(self.qtPath, "plugins") + if os.path.exists(pluginPath): + self.pluginPath = pluginPath + + def usesFramework(self, name: str) -> bool: + nameDot = "{}.".format(name) + libNameDot = "lib{}.".format(name) + for framework in self.deployedFrameworks: + if framework.endswith(".framework"): + if framework.startswith(nameDot): + return True + elif framework.endswith(".dylib"): + if framework.startswith(libNameDot): + return True + return False + +def getFrameworks(binaryPath: str, verbose: int) -> List[FrameworkInfo]: + if verbose >= 3: + print("Inspecting with otool: " + binaryPath) + otoolbin=os.getenv("OTOOL", "otool") + otool = subprocess.Popen([otoolbin, "-L", binaryPath], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + o_stdout, o_stderr = otool.communicate() + if otool.returncode != 0: + if verbose >= 1: + sys.stderr.write(o_stderr) + sys.stderr.flush() + raise RuntimeError("otool failed with return code {}".format(otool.returncode)) + + otoolLines = o_stdout.split("\n") + otoolLines.pop(0) # First line is the inspected binary + if ".framework" in binaryPath or binaryPath.endswith(".dylib"): + otoolLines.pop(0) # Frameworks and dylibs list themselves as a dependency. + + libraries = [] + for line in otoolLines: + line = line.replace("@loader_path", os.path.dirname(binaryPath)) + info = FrameworkInfo.fromOtoolLibraryLine(line.strip()) + if info is not None: + if verbose >= 3: + print("Found framework:") + print(info) + libraries.append(info) + + return libraries + +def runInstallNameTool(action: str, *args): + installnametoolbin=os.getenv("INSTALLNAMETOOL", "install_name_tool") + subprocess.check_call([installnametoolbin, "-"+action] + list(args)) + +def changeInstallName(oldName: str, newName: str, binaryPath: str, verbose: int): + if verbose >= 3: + print("Using install_name_tool:") + print(" in", binaryPath) + print(" change reference", oldName) + print(" to", newName) + runInstallNameTool("change", oldName, newName, binaryPath) + +def changeIdentification(id: str, binaryPath: str, verbose: int): + if verbose >= 3: + print("Using install_name_tool:") + print(" change identification in", binaryPath) + print(" to", id) + runInstallNameTool("id", id, binaryPath) + +def runStrip(binaryPath: str, verbose: int): + stripbin=os.getenv("STRIP", "strip") + if verbose >= 3: + print("Using strip:") + print(" stripped", binaryPath) + subprocess.check_call([stripbin, "-x", binaryPath]) + +def copyFramework(framework: FrameworkInfo, path: str, verbose: int) -> Optional[str]: + if framework.sourceFilePath.startswith("Qt"): + #standard place for Nokia Qt installer's frameworks + fromPath = "/Library/Frameworks/" + framework.sourceFilePath + else: + fromPath = framework.sourceFilePath + toDir = os.path.join(path, framework.destinationDirectory) + toPath = os.path.join(toDir, framework.binaryName) + + if not os.path.exists(fromPath): + raise RuntimeError("No file at " + fromPath) + + if os.path.exists(toPath): + return None # Already there + + if not os.path.exists(toDir): + os.makedirs(toDir) + + shutil.copy2(fromPath, toPath) + if verbose >= 3: + print("Copied:", fromPath) + print(" to:", toPath) + + permissions = os.stat(toPath) + if not permissions.st_mode & stat.S_IWRITE: + os.chmod(toPath, permissions.st_mode | stat.S_IWRITE) + + if not framework.isDylib(): # Copy resources for real frameworks + + linkfrom = os.path.join(path, "Contents","Frameworks", framework.frameworkName, "Versions", "Current") + linkto = framework.version + if not os.path.exists(linkfrom): + os.symlink(linkto, linkfrom) + if verbose >= 2: + print("Linked:", linkfrom, "->", linkto) + fromResourcesDir = framework.sourceResourcesDirectory + if os.path.exists(fromResourcesDir): + toResourcesDir = os.path.join(path, framework.destinationResourcesDirectory) + shutil.copytree(fromResourcesDir, toResourcesDir, symlinks=True) + if verbose >= 3: + print("Copied resources:", fromResourcesDir) + print(" to:", toResourcesDir) + fromContentsDir = framework.sourceVersionContentsDirectory + if not os.path.exists(fromContentsDir): + fromContentsDir = framework.sourceContentsDirectory + if os.path.exists(fromContentsDir): + toContentsDir = os.path.join(path, framework.destinationVersionContentsDirectory) + shutil.copytree(fromContentsDir, toContentsDir, symlinks=True) + if verbose >= 3: + print("Copied Contents:", fromContentsDir) + print(" to:", toContentsDir) + elif framework.frameworkName.startswith("libQtGui"): # Copy qt_menu.nib (applies to non-framework layout) + qtMenuNibSourcePath = os.path.join(framework.frameworkDirectory, "Resources", "qt_menu.nib") + qtMenuNibDestinationPath = os.path.join(path, "Contents", "Resources", "qt_menu.nib") + if os.path.exists(qtMenuNibSourcePath) and not os.path.exists(qtMenuNibDestinationPath): + shutil.copytree(qtMenuNibSourcePath, qtMenuNibDestinationPath, symlinks=True) + if verbose >= 3: + print("Copied for libQtGui:", qtMenuNibSourcePath) + print(" to:", qtMenuNibDestinationPath) + + return toPath + +def deployFrameworks(frameworks: List[FrameworkInfo], bundlePath: str, binaryPath: str, strip: bool, verbose: int, deploymentInfo: Optional[DeploymentInfo] = None) -> DeploymentInfo: + if deploymentInfo is None: + deploymentInfo = DeploymentInfo() + + while len(frameworks) > 0: + framework = frameworks.pop(0) + deploymentInfo.deployedFrameworks.append(framework.frameworkName) + + if verbose >= 2: + print("Processing", framework.frameworkName, "...") + + # Get the Qt path from one of the Qt frameworks + if deploymentInfo.qtPath is None and framework.isQtFramework(): + deploymentInfo.detectQtPath(framework.frameworkDirectory) + + if framework.installName.startswith("@executable_path") or framework.installName.startswith(bundlePath): + if verbose >= 2: + print(framework.frameworkName, "already deployed, skipping.") + continue + + # install_name_tool the new id into the binary + changeInstallName(framework.installName, framework.deployedInstallName, binaryPath, verbose) + + # Copy framework to app bundle. + deployedBinaryPath = copyFramework(framework, bundlePath, verbose) + # Skip the rest if already was deployed. + if deployedBinaryPath is None: + continue + + if strip: + runStrip(deployedBinaryPath, verbose) + + # install_name_tool it a new id. + changeIdentification(framework.deployedInstallName, deployedBinaryPath, verbose) + # Check for framework dependencies + dependencies = getFrameworks(deployedBinaryPath, verbose) + + for dependency in dependencies: + changeInstallName(dependency.installName, dependency.deployedInstallName, deployedBinaryPath, verbose) + + # Deploy framework if necessary. + if dependency.frameworkName not in deploymentInfo.deployedFrameworks and dependency not in frameworks: + frameworks.append(dependency) + + return deploymentInfo + +def deployFrameworksForAppBundle(applicationBundle: ApplicationBundleInfo, strip: bool, verbose: int) -> DeploymentInfo: + frameworks = getFrameworks(applicationBundle.binaryPath, verbose) + if len(frameworks) == 0 and verbose >= 1: + print("Warning: Could not find any external frameworks to deploy in {}.".format(applicationBundle.path)) + return DeploymentInfo() + else: + return deployFrameworks(frameworks, applicationBundle.path, applicationBundle.binaryPath, strip, verbose) + +def deployPlugins(appBundleInfo: ApplicationBundleInfo, deploymentInfo: DeploymentInfo, strip: bool, verbose: int): + # Lookup available plugins, exclude unneeded + plugins = [] + if deploymentInfo.pluginPath is None: + return + for dirpath, dirnames, filenames in os.walk(deploymentInfo.pluginPath): + pluginDirectory = os.path.relpath(dirpath, deploymentInfo.pluginPath) + if pluginDirectory == "designer": + # Skip designer plugins + continue + elif pluginDirectory == "printsupport": + # Skip printsupport plugins + continue + elif pluginDirectory == "imageformats": + # Skip imageformats plugins + continue + elif pluginDirectory == "sqldrivers": + # Deploy the sql plugins only if QtSql is in use + if not deploymentInfo.usesFramework("QtSql"): + continue + elif pluginDirectory == "script": + # Deploy the script plugins only if QtScript is in use + if not deploymentInfo.usesFramework("QtScript"): + continue + elif pluginDirectory == "qmltooling" or pluginDirectory == "qml1tooling": + # Deploy the qml plugins only if QtDeclarative is in use + if not deploymentInfo.usesFramework("QtDeclarative"): + continue + elif pluginDirectory == "bearer": + # Deploy the bearer plugins only if QtNetwork is in use + if not deploymentInfo.usesFramework("QtNetwork"): + continue + elif pluginDirectory == "position": + # Deploy the position plugins only if QtPositioning is in use + if not deploymentInfo.usesFramework("QtPositioning"): + continue + elif pluginDirectory == "sensors" or pluginDirectory == "sensorgestures": + # Deploy the sensor plugins only if QtSensors is in use + if not deploymentInfo.usesFramework("QtSensors"): + continue + elif pluginDirectory == "audio" or pluginDirectory == "playlistformats": + # Deploy the audio plugins only if QtMultimedia is in use + if not deploymentInfo.usesFramework("QtMultimedia"): + continue + elif pluginDirectory == "mediaservice": + # Deploy the mediaservice plugins only if QtMultimediaWidgets is in use + if not deploymentInfo.usesFramework("QtMultimediaWidgets"): + continue + elif pluginDirectory == "canbus": + # Deploy the canbus plugins only if QtSerialBus is in use + if not deploymentInfo.usesFramework("QtSerialBus"): + continue + elif pluginDirectory == "webview": + # Deploy the webview plugins only if QtWebView is in use + if not deploymentInfo.usesFramework("QtWebView"): + continue + elif pluginDirectory == "gamepads": + # Deploy the webview plugins only if QtGamepad is in use + if not deploymentInfo.usesFramework("QtGamepad"): + continue + elif pluginDirectory == "geoservices": + # Deploy the webview plugins only if QtLocation is in use + if not deploymentInfo.usesFramework("QtLocation"): + continue + elif pluginDirectory == "texttospeech": + # Deploy the texttospeech plugins only if QtTextToSpeech is in use + if not deploymentInfo.usesFramework("QtTextToSpeech"): + continue + elif pluginDirectory == "virtualkeyboard": + # Deploy the virtualkeyboard plugins only if QtVirtualKeyboard is in use + if not deploymentInfo.usesFramework("QtVirtualKeyboard"): + continue + elif pluginDirectory == "sceneparsers": + # Deploy the virtualkeyboard plugins only if Qt3DCore is in use + if not deploymentInfo.usesFramework("Qt3DCore"): + continue + elif pluginDirectory == "renderplugins": + # Deploy the renderplugins plugins only if Qt3DCore is in use + if not deploymentInfo.usesFramework("Qt3DCore"): + continue + elif pluginDirectory == "geometryloaders": + # Deploy the geometryloaders plugins only if Qt3DCore is in use + if not deploymentInfo.usesFramework("Qt3DCore"): + continue + + for pluginName in filenames: + pluginPath = os.path.join(pluginDirectory, pluginName) + if pluginName.endswith("_debug.dylib"): + # Skip debug plugins + continue + elif pluginPath == "imageformats/libqsvg.dylib" or pluginPath == "iconengines/libqsvgicon.dylib": + # Deploy the svg plugins only if QtSvg is in use + if not deploymentInfo.usesFramework("QtSvg"): + continue + elif pluginPath == "accessible/libqtaccessiblecompatwidgets.dylib": + # Deploy accessibility for Qt3Support only if the Qt3Support is in use + if not deploymentInfo.usesFramework("Qt3Support"): + continue + elif pluginPath == "graphicssystems/libqglgraphicssystem.dylib": + # Deploy the opengl graphicssystem plugin only if QtOpenGL is in use + if not deploymentInfo.usesFramework("QtOpenGL"): + continue + elif pluginPath == "accessible/libqtaccessiblequick.dylib": + # Deploy the accessible qtquick plugin only if QtQuick is in use + if not deploymentInfo.usesFramework("QtQuick"): + continue + elif pluginPath == "platforminputcontexts/libqtvirtualkeyboardplugin.dylib": + # Deploy the virtualkeyboardplugin plugin only if QtVirtualKeyboard is in use + if not deploymentInfo.usesFramework("QtVirtualKeyboard"): + continue + + plugins.append((pluginDirectory, pluginName)) + + for pluginDirectory, pluginName in plugins: + if verbose >= 2: + print("Processing plugin", os.path.join(pluginDirectory, pluginName), "...") + + sourcePath = os.path.join(deploymentInfo.pluginPath, pluginDirectory, pluginName) + destinationDirectory = os.path.join(appBundleInfo.pluginPath, pluginDirectory) + if not os.path.exists(destinationDirectory): + os.makedirs(destinationDirectory) + + destinationPath = os.path.join(destinationDirectory, pluginName) + shutil.copy2(sourcePath, destinationPath) + if verbose >= 3: + print("Copied:", sourcePath) + print(" to:", destinationPath) + + if strip: + runStrip(destinationPath, verbose) + + dependencies = getFrameworks(destinationPath, verbose) + + for dependency in dependencies: + changeInstallName(dependency.installName, dependency.deployedInstallName, destinationPath, verbose) + + # Deploy framework if necessary. + if dependency.frameworkName not in deploymentInfo.deployedFrameworks: + deployFrameworks([dependency], appBundleInfo.path, destinationPath, strip, verbose, deploymentInfo) + +qt_conf="""[Paths] +Translations=Resources +Plugins=PlugIns +""" + +ap = ArgumentParser(description="""Improved version of macdeployqt. + +Outputs a ready-to-deploy app in a folder "dist" and optionally wraps it in a .dmg file. +Note, that the "dist" folder will be deleted before deploying on each run. + +Optionally, Qt translation files (.qm) and additional resources can be added to the bundle. + +Also optionally signs the .app bundle; set the CODESIGNARGS environment variable to pass arguments +to the codesign tool. +E.g. CODESIGNARGS='--sign "Developer ID Application: ..." --keychain /encrypted/foo.keychain'""") + +ap.add_argument("app_bundle", nargs=1, metavar="app-bundle", help="application bundle to be deployed") +ap.add_argument("-verbose", type=int, nargs=1, default=[1], metavar="<0-3>", help="0 = no output, 1 = error/warning (default), 2 = normal, 3 = debug") +ap.add_argument("-no-plugins", dest="plugins", action="store_false", default=True, help="skip plugin deployment") +ap.add_argument("-no-strip", dest="strip", action="store_false", default=True, help="don't run 'strip' on the binaries") +ap.add_argument("-sign", dest="sign", action="store_true", default=False, help="sign .app bundle with codesign tool") +ap.add_argument("-dmg", nargs="?", const="", metavar="basename", help="create a .dmg disk image; if basename is not specified, a camel-cased version of the app name is used") +ap.add_argument("-fancy", nargs=1, metavar="plist", default=[], help="make a fancy looking disk image using the given plist file with instructions; requires -dmg to work") +ap.add_argument("-add-qt-tr", nargs=1, metavar="languages", default=[], help="add Qt translation files to the bundle's resources; the language list must be separated with commas, not with whitespace") +ap.add_argument("-translations-dir", nargs=1, metavar="path", default=None, help="Path to Qt's translation files") +ap.add_argument("-add-resources", nargs="+", metavar="path", default=[], help="list of additional files or folders to be copied into the bundle's resources; must be the last argument") +ap.add_argument("-volname", nargs=1, metavar="volname", default=[], help="custom volume name for dmg") + +config = ap.parse_args() + +verbose = config.verbose[0] + +# ------------------------------------------------ + +app_bundle = config.app_bundle[0] + +if not os.path.exists(app_bundle): + if verbose >= 1: + sys.stderr.write("Error: Could not find app bundle \"{}\"\n".format(app_bundle)) + sys.exit(1) + +app_bundle_name = os.path.splitext(os.path.basename(app_bundle))[0] + +# ------------------------------------------------ +translations_dir = None +if config.translations_dir and config.translations_dir[0]: + if os.path.exists(config.translations_dir[0]): + translations_dir = config.translations_dir[0] + else: + if verbose >= 1: + sys.stderr.write("Error: Could not find translation dir \"{}\"\n".format(translations_dir)) + sys.exit(1) +# ------------------------------------------------ + +for p in config.add_resources: + if verbose >= 3: + print("Checking for \"%s\"..." % p) + if not os.path.exists(p): + if verbose >= 1: + sys.stderr.write("Error: Could not find additional resource file \"{}\"\n".format(p)) + sys.exit(1) + +# ------------------------------------------------ + +if len(config.fancy) == 1: + if verbose >= 3: + print("Fancy: Importing plistlib...") + try: + import plistlib + except ImportError: + if verbose >= 1: + sys.stderr.write("Error: Could not import plistlib which is required for fancy disk images.\n") + sys.exit(1) + + p = config.fancy[0] + if verbose >= 3: + print("Fancy: Loading \"{}\"...".format(p)) + if not os.path.exists(p): + if verbose >= 1: + sys.stderr.write("Error: Could not find fancy disk image plist at \"{}\"\n".format(p)) + sys.exit(1) + + try: + fancy = plistlib.readPlist(p) + except: + if verbose >= 1: + sys.stderr.write("Error: Could not parse fancy disk image plist at \"{}\"\n".format(p)) + sys.exit(1) + + try: + assert "window_bounds" not in fancy or (isinstance(fancy["window_bounds"], list) and len(fancy["window_bounds"]) == 4) + assert "background_picture" not in fancy or isinstance(fancy["background_picture"], str) + assert "icon_size" not in fancy or isinstance(fancy["icon_size"], int) + assert "applications_symlink" not in fancy or isinstance(fancy["applications_symlink"], bool) + if "items_position" in fancy: + assert isinstance(fancy["items_position"], dict) + for key, value in fancy["items_position"].items(): + assert isinstance(value, list) and len(value) == 2 and isinstance(value[0], int) and isinstance(value[1], int) + except: + if verbose >= 1: + sys.stderr.write("Error: Bad format of fancy disk image plist at \"{}\"\n".format(p)) + sys.exit(1) + + if "background_picture" in fancy: + bp = fancy["background_picture"] + if verbose >= 3: + print("Fancy: Resolving background picture \"{}\"...".format(bp)) + if not os.path.exists(bp): + bp = os.path.join(os.path.dirname(p), bp) + if not os.path.exists(bp): + if verbose >= 1: + sys.stderr.write("Error: Could not find background picture at \"{}\" or \"{}\"\n".format(fancy["background_picture"], bp)) + sys.exit(1) + else: + fancy["background_picture"] = bp +else: + fancy = None + +# ------------------------------------------------ + +if os.path.exists("dist"): + if verbose >= 2: + print("+ Removing old dist folder +") + + shutil.rmtree("dist") + +# ------------------------------------------------ + +if len(config.volname) == 1: + volname = config.volname[0] +else: + volname = app_bundle_name + +# ------------------------------------------------ + +target = os.path.join("dist", "Komodo-Qt.app") + +if verbose >= 2: + print("+ Copying source bundle +") +if verbose >= 3: + print(app_bundle, "->", target) + +os.mkdir("dist") +shutil.copytree(app_bundle, target, symlinks=True) + +applicationBundle = ApplicationBundleInfo(target) + +# ------------------------------------------------ + +if verbose >= 2: + print("+ Deploying frameworks +") + +try: + deploymentInfo = deployFrameworksForAppBundle(applicationBundle, config.strip, verbose) + if deploymentInfo.qtPath is None: + deploymentInfo.qtPath = os.getenv("QTDIR", None) + if deploymentInfo.qtPath is None: + if verbose >= 1: + sys.stderr.write("Warning: Could not detect Qt's path, skipping plugin deployment!\n") + config.plugins = False +except RuntimeError as e: + if verbose >= 1: + sys.stderr.write("Error: {}\n".format(str(e))) + sys.exit(1) + +# ------------------------------------------------ + +if config.plugins: + if verbose >= 2: + print("+ Deploying plugins +") + + try: + deployPlugins(applicationBundle, deploymentInfo, config.strip, verbose) + except RuntimeError as e: + if verbose >= 1: + sys.stderr.write("Error: {}\n".format(str(e))) + sys.exit(1) + +# ------------------------------------------------ + +if len(config.add_qt_tr) == 0: + add_qt_tr = [] +else: + if translations_dir is not None: + qt_tr_dir = translations_dir + else: + if deploymentInfo.qtPath is not None: + qt_tr_dir = os.path.join(deploymentInfo.qtPath, "translations") + else: + sys.stderr.write("Error: Could not find Qt translation path\n") + sys.exit(1) + add_qt_tr = ["qt_{}.qm".format(lng) for lng in config.add_qt_tr[0].split(",")] + for lng_file in add_qt_tr: + p = os.path.join(qt_tr_dir, lng_file) + if verbose >= 3: + print("Checking for \"{}\"...".format(p)) + if not os.path.exists(p): + if verbose >= 1: + sys.stderr.write("Error: Could not find Qt translation file \"{}\"\n".format(lng_file)) + sys.exit(1) + +# ------------------------------------------------ + +if verbose >= 2: + print("+ Installing qt.conf +") + +with open(os.path.join(applicationBundle.resourcesPath, "qt.conf"), "wb") as f: + f.write(qt_conf.encode()) + +# ------------------------------------------------ + +if len(add_qt_tr) > 0 and verbose >= 2: + print("+ Adding Qt translations +") + +for lng_file in add_qt_tr: + if verbose >= 3: + print(os.path.join(qt_tr_dir, lng_file), "->", os.path.join(applicationBundle.resourcesPath, lng_file)) + shutil.copy2(os.path.join(qt_tr_dir, lng_file), os.path.join(applicationBundle.resourcesPath, lng_file)) + +# ------------------------------------------------ + +if len(config.add_resources) > 0 and verbose >= 2: + print("+ Adding additional resources +") + +for p in config.add_resources: + t = os.path.join(applicationBundle.resourcesPath, os.path.basename(p)) + if verbose >= 3: + print(p, "->", t) + if os.path.isdir(p): + shutil.copytree(p, t, symlinks=True) + else: + shutil.copy2(p, t) + +# ------------------------------------------------ + +if config.sign and 'CODESIGNARGS' not in os.environ: + print("You must set the CODESIGNARGS environment variable. Skipping signing.") +elif config.sign: + if verbose >= 1: + print("Code-signing app bundle {}".format(target)) + subprocess.check_call("codesign --force {} {}".format(os.environ['CODESIGNARGS'], target), shell=True) + +# ------------------------------------------------ + +if config.dmg is not None: + + def runHDIUtil(verb: str, image_basename: str, **kwargs) -> int: + hdiutil_args = ["hdiutil", verb, image_basename + ".dmg"] + if "capture_stdout" in kwargs: + del kwargs["capture_stdout"] + run = subprocess.check_output + else: + if verbose < 2: + hdiutil_args.append("-quiet") + elif verbose >= 3: + hdiutil_args.append("-verbose") + run = subprocess.check_call + + for key, value in kwargs.items(): + hdiutil_args.append("-" + key) + if value is not True: + hdiutil_args.append(str(value)) + + return run(hdiutil_args, universal_newlines=True) + + if verbose >= 2: + if fancy is None: + print("+ Creating .dmg disk image +") + else: + print("+ Preparing .dmg disk image +") + + if config.dmg != "": + dmg_name = config.dmg + else: + spl = app_bundle_name.split(" ") + dmg_name = spl[0] + "".join(p.capitalize() for p in spl[1:]) + + if fancy is None: + try: + runHDIUtil("create", dmg_name, srcfolder="dist", format="UDBZ", volname=volname, ov=True) + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) + else: + if verbose >= 3: + print("Determining size of \"dist\"...") + size = 0 + for path, dirs, files in os.walk("dist"): + for file in files: + size += os.path.getsize(os.path.join(path, file)) + size += int(size * 0.15) + + if verbose >= 3: + print("Creating temp image for modification...") + try: + runHDIUtil("create", dmg_name + ".temp", srcfolder="dist", format="UDRW", size=size, volname=volname, ov=True) + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) + + if verbose >= 3: + print("Attaching temp image...") + try: + output = runHDIUtil("attach", dmg_name + ".temp", readwrite=True, noverify=True, noautoopen=True, capture_stdout=True) + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) + + m = re.search(r"/Volumes/(.+$)", output) + disk_root = m.group(0) + disk_name = m.group(1) + + if verbose >= 2: + print("+ Applying fancy settings +") + + if "background_picture" in fancy: + bg_path = os.path.join(disk_root, ".background", os.path.basename(fancy["background_picture"])) + os.mkdir(os.path.dirname(bg_path)) + if verbose >= 3: + print(fancy["background_picture"], "->", bg_path) + shutil.copy2(fancy["background_picture"], bg_path) + else: + bg_path = None + + if fancy.get("applications_symlink", False): + os.symlink("/Applications", os.path.join(disk_root, "Applications")) + + # The Python appscript package broke with OSX 10.8 and isn't being fixed. + # So we now build up an AppleScript string and use the osascript command + # to make the .dmg file pretty: + appscript = Template( """ + on run argv + tell application "Finder" + tell disk "$disk" + open + set current view of container window to icon view + set toolbar visible of container window to false + set statusbar visible of container window to false + set the bounds of container window to {$window_bounds} + set theViewOptions to the icon view options of container window + set arrangement of theViewOptions to not arranged + set icon size of theViewOptions to $icon_size + $background_commands + $items_positions + close -- close/reopen works around a bug... + open + update without registering applications + delay 5 + eject + end tell + end tell + end run + """) + + itemscript = Template('set position of item "${item}" of container window to {${position}}') + items_positions = [] + if "items_position" in fancy: + for name, position in fancy["items_position"].items(): + params = { "item" : name, "position" : ",".join([str(p) for p in position]) } + items_positions.append(itemscript.substitute(params)) + + params = { + "disk" : volname, + "window_bounds" : "300,300,800,620", + "icon_size" : "96", + "background_commands" : "", + "items_positions" : "\n ".join(items_positions) + } + if "window_bounds" in fancy: + params["window_bounds"] = ",".join([str(p) for p in fancy["window_bounds"]]) + if "icon_size" in fancy: + params["icon_size"] = str(fancy["icon_size"]) + if bg_path is not None: + # Set background file, then call SetFile to make it invisible. + # (note: making it invisible first makes set background picture fail) + bgscript = Template("""set background picture of theViewOptions to file ".background:$bgpic" + do shell script "SetFile -a V /Volumes/$disk/.background/$bgpic" """) + params["background_commands"] = bgscript.substitute({"bgpic" : os.path.basename(bg_path), "disk" : params["disk"]}) + + s = appscript.substitute(params) + if verbose >= 2: + print("Running AppleScript:") + print(s) + + p = subprocess.Popen(['osascript', '-'], stdin=subprocess.PIPE) + p.communicate(input=s.encode('utf-8')) + if p.returncode: + print("Error running osascript.") + + if verbose >= 2: + print("+ Finalizing .dmg disk image +") + time.sleep(5) + + try: + runHDIUtil("convert", dmg_name + ".temp", format="UDBZ", o=dmg_name + ".dmg", ov=True) + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) + + os.unlink(dmg_name + ".temp.dmg") + +# ------------------------------------------------ + +if verbose >= 2: + print("+ Done +") + +sys.exit(0) diff --git a/depends/Makefile b/depends/Makefile index 04965b10..d1a97e9a 100644 --- a/depends/Makefile +++ b/depends/Makefile @@ -157,7 +157,7 @@ install: check-packages $(host_prefix)/share/config.site download-one: check-sources $(all_sources) download-osx: - @$(MAKE) -s HOST=x86_64-apple-darwin11 download-one + @$(MAKE) -s HOST=x86_64-apple-darwin18 download-one download-linux: @$(MAKE) -s HOST=x86_64-unknown-linux-gnu download-one download-win: diff --git a/depends/builders/darwin.mk b/depends/builders/darwin.mk index f9b066fc..f4103fc1 100644 --- a/depends/builders/darwin.mk +++ b/depends/builders/darwin.mk @@ -1,17 +1,17 @@ -build_darwin_CC = gcc-6 -build_darwin_CXX = g++-6 -build_darwin_AR: = $(shell xcrun -f ar) -build_darwin_RANLIB: = $(shell xcrun -f ranlib) -build_darwin_STRIP: = $(shell xcrun -f strip) -build_darwin_OTOOL: = $(shell xcrun -f otool) -build_darwin_NM: = $(shell xcrun -f nm) +build_darwin_CC:=$(shell xcrun -f clang) --sysroot $(shell xcrun --show-sdk-path) +build_darwin_CXX:=$(shell xcrun -f clang++) --sysroot $(shell xcrun --show-sdk-path) +build_darwin_AR:=$(shell xcrun -f ar) +build_darwin_RANLIB:=$(shell xcrun -f ranlib) +build_darwin_STRIP:=$(shell xcrun -f strip) +build_darwin_OTOOL:=$(shell xcrun -f otool) +build_darwin_NM:=$(shell xcrun -f nm) build_darwin_INSTALL_NAME_TOOL:=$(shell xcrun -f install_name_tool) -build_darwin_SHA256SUM = shasum -a 256 -build_darwin_DOWNLOAD = curl --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -L -f -o +build_darwin_SHA256SUM=shasum -a 256 +build_darwin_DOWNLOAD=curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -o #darwin host on darwin builder. overrides darwin host preferences. -darwin_CC= gcc-6 -darwin_CXX= g++-6 +darwin_CC=$(shell xcrun -f clang) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(shell xcrun --show-sdk-path) +darwin_CXX:=$(shell xcrun -f clang++) -mmacosx-version-min=$(OSX_MIN_VERSION) -stdlib=libc++ --sysroot $(shell xcrun --show-sdk-path) darwin_AR:=$(shell xcrun -f ar) darwin_RANLIB:=$(shell xcrun -f ranlib) darwin_STRIP:=$(shell xcrun -f strip) @@ -19,4 +19,5 @@ darwin_LIBTOOL:=$(shell xcrun -f libtool) darwin_OTOOL:=$(shell xcrun -f otool) darwin_NM:=$(shell xcrun -f nm) darwin_INSTALL_NAME_TOOL:=$(shell xcrun -f install_name_tool) +darwin_native_binutils= darwin_native_toolchain= diff --git a/depends/funcs.mk b/depends/funcs.mk index 11c5843e..d00de578 100644 --- a/depends/funcs.mk +++ b/depends/funcs.mk @@ -58,9 +58,13 @@ $(eval $(1)_build_id_long:=$(1)-$($(1)_version)-$($(1)_recipe_hash)-$(release_ty $(eval $(1)_build_id:=$(shell echo -n "$($(1)_build_id_long)" | $(build_SHA256SUM) | cut -c-$(HASH_LENGTH))) final_build_id_long+=$($(package)_build_id_long) -#override platform specific files and hashes -$(eval $(1)_file_name=$(if $($(1)_file_name_$(host_os)),$($(1)_file_name_$(host_os)),$($(1)_file_name))) -$(eval $(1)_sha256_hash=$(if $($(1)_sha256_hash_$(host_os)),$($(1)_sha256_hash_$(host_os)),$($(1)_sha256_hash))) +# override platform specific files and hashes +# $(eval $(1)_file_name=$(if $($(1)_file_name_$(host_os)),$($(1)_file_name_$(host_os)),$($(1)_file_name))) +# $(eval $(1)_sha256_hash=$(if $($(1)_sha256_hash_$(host_os)),$($(1)_sha256_hash_$(host_os)),$($(1)_sha256_hash))) +$(eval $(1)_file_name=$(if $($(1)_exact_file_name),$($(1)_exact_file_name),$(if $($(1)_file_name_$(host_arch)_$(host_os)),$($(1)_file_name_$(host_arch)_$(host_os)),$(if $($(1)_file_name_$(host_os)),$($(1)_file_name_$(host_os)),$($(1)_file_name))))) +$(eval $(1)_sha256_hash=$(if $($(1)_exact_sha256_hash),$($(1)_exact_sha256_hash),$(if $($(1)_sha256_hash_$(host_arch)_$(host_os)),$($(1)_sha256_hash_$(host_arch)_$(host_os)),$(if $($(1)_sha256_hash_$(host_os)),$($(1)_sha256_hash_$(host_os)),$($(1)_sha256_hash))))) +$(eval $(1)_download_file=$(if $($(1)_exact_download_file),$($(1)_exact_download_file),$(if $($(1)_download_file_$(host_arch)_$(host_os)),$($(1)_download_file_$(host_arch)_$(host_os)),$(if $($(1)_download_file_$(host_os)),$($(1)_download_file_$(host_os)),$(if $($(1)_download_file),$($(1)_download_file),$($(1)_file_name)))))) +$(eval $(1)_download_path=$(if $($(1)_exact_download_path),$($(1)_exact_download_path),$(if $($(1)_download_path_$(host_arch)_$(host_os)),$($(1)_download_path_$(host_arch)_$(host_os)),$(if $($(1)_download_path_$(host_os)),$($(1)_download_path_$(host_os)),$($(1)_download_path))))) #compute package-specific paths $(1)_build_subdir?=. @@ -145,6 +149,7 @@ $(1)_config_env+=$($(1)_config_env_$(host_arch)_$(host_os)) $($(1)_config_env_$( $(1)_config_env+=PKG_CONFIG_LIBDIR=$($($(1)_type)_prefix)/lib/pkgconfig $(1)_config_env+=PKG_CONFIG_PATH=$($($(1)_type)_prefix)/share/pkgconfig +$(1)_config_env+=CMAKE_MODULE_PATH=$($($(1)_type)_prefix)/lib/cmake $(1)_config_env+=PATH="$(build_prefix)/bin:$(PATH)" $(1)_build_env+=PATH="$(build_prefix)/bin:$(PATH)" $(1)_stage_env+=PATH="$(build_prefix)/bin:$(PATH)" @@ -171,6 +176,20 @@ endif ifneq ($($(1)_ldflags),) $(1)_autoconf += LDFLAGS="$$($(1)_ldflags)" endif + +$(1)_cmake=env CC="$$($(1)_cc)" \ + CFLAGS="$$($(1)_cppflags) $$($(1)_cflags)" \ + CXX="$$($(1)_cxx)" \ + CXXFLAGS="$$($(1)_cppflags) $$($(1)_cxxflags)" \ + LDFLAGS="$$($(1)_ldflags)" \ + cmake -DCMAKE_INSTALL_PREFIX:PATH="$$($($(1)_type)_prefix)" +ifneq ($($(1)_type),build) +ifneq ($(host),$(build)) +$(1)_cmake += -DCMAKE_SYSTEM_NAME=$($(host_os)_cmake_system) +$(1)_cmake += -DCMAKE_C_COMPILER_TARGET=$(host) +$(1)_cmake += -DCMAKE_CXX_COMPILER_TARGET=$(host) +endif +endif endef define int_add_cmds diff --git a/depends/hosts/darwin.mk b/depends/hosts/darwin.mk index 52c0003e..6099fd4c 100644 --- a/depends/hosts/darwin.mk +++ b/depends/hosts/darwin.mk @@ -1,9 +1,35 @@ -OSX_MIN_VERSION=10.7 -OSX_SDK_VERSION=10.9 -OSX_SDK=$(SDK_PATH)/MacOSX$(OSX_SDK_VERSION).sdk -LD64_VERSION=241.9 -darwin_CC=gcc-5 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) -darwin_CXX=g++-5 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) +OSX_MIN_VERSION=10.12 +OSX_SDK_VERSION=10.15.1 +XCODE_VERSION=11.3.1 +XCODE_BUILD_ID=11C505 +LD64_VERSION=530 + +OSX_SDK=$(SDK_PATH)/Xcode-$(XCODE_VERSION)-$(XCODE_BUILD_ID)-extracted-SDK-with-libcxx-headers + +# Flag explanations: +# +# -mlinker-version +# +# Ensures that modern linker features are enabled. See here for more +# details: https://github.com/bitcoin/bitcoin/pull/19407. +# +# -B$(build_prefix)/bin +# +# Explicitly point to our binaries (e.g. cctools) so that they are +# ensured to be found and preferred over other possibilities. +# +# -nostdinc++ -isystem $(OSX_SDK)/usr/include/c++/v1 +# +# Forces clang to use the libc++ headers from our SDK and completely +# forget about the libc++ headers from the standard directories +# +# TODO: Once we start requiring a clang version that has the +# -stdlib++-isystem flag first introduced here: +# https://reviews.llvm.org/D64089, we should use that instead. Read the +# differential summary there for more details. +# +darwin_CC=clang -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) -B$(build_prefix)/bin +darwin_CXX=clang++ -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -stdlib=libc++ -mlinker-version=$(LD64_VERSION) -B$(build_prefix)/bin -nostdinc++ -isystem $(OSX_SDK)/usr/include/c++/v1 darwin_CFLAGS=-pipe darwin_CXXFLAGS=$(darwin_CFLAGS) @@ -14,4 +40,11 @@ darwin_release_CXXFLAGS=$(darwin_release_CFLAGS) darwin_debug_CFLAGS=-O1 darwin_debug_CXXFLAGS=$(darwin_debug_CFLAGS) +darwin_native_binutils=native_cctools +ifeq ($(strip $(FORCE_USE_SYSTEM_CLANG)),) darwin_native_toolchain=native_cctools +else +darwin_native_toolchain= +endif + +darwin_cmake_system=Darwin diff --git a/depends/hosts/mingw32.mk b/depends/hosts/mingw32.mk index 65ab1702..aafc9994 100644 --- a/depends/hosts/mingw32.mk +++ b/depends/hosts/mingw32.mk @@ -1,6 +1,6 @@ mingw32_CC=x86_64-w64-mingw32-gcc-posix mingw32_CXX=x86_64-w64-mingw32-g++-posix -mingw32_CFLAGS=-pipe -std=c11 +mingw32_CFLAGS=-pipe mingw32_CXXFLAGS=$(mingw32_CFLAGS) -std=c++11 mingw32_release_CFLAGS=-O2 diff --git a/depends/packages/bdb.mk b/depends/packages/bdb.mk index 657fb51b..f060b638 100644 --- a/depends/packages/bdb.mk +++ b/depends/packages/bdb.mk @@ -9,6 +9,16 @@ define $(package)_set_vars $(package)_config_opts=--disable-shared --enable-cxx --disable-replication $(package)_config_opts_mingw32=--enable-mingw $(package)_config_opts_linux=--with-pic +$(package)_cxxflags=-std=c++11 +ifneq ($(build_os),darwin) +$(package)_config_opts_darwin=--disable-atomicsupport +endif +endef + +define $(package)_preprocess_cmds + sed -i.old 's/WinIoCtl.h/winioctl.h/g' src/dbinc/win_db.h && \ + sed -i.old 's/__atomic_compare_exchange\\(/__atomic_compare_exchange_db(/' src/dbinc/atomic.h && \ + sed -i.old 's/atomic_init/atomic_init_db/' src/dbinc/atomic.h src/mp/mp_region.c src/mp/mp_mvcc.c src/mp/mp_fget.c src/mutex/mut_method.c src/mutex/mut_tas.c endef define $(package)_config_cmds @@ -18,7 +28,9 @@ endef ifeq ($(build_os),darwin) define $(package)_preprocess_cmds - sed -i -e "s/WinIoCtl.h/winioctl.h/g" src/dbinc/win_db.h + sed -i -e "s/WinIoCtl.h/winioctl.h/g" src/dbinc/win_db.h && \ + sed -i.old 's/__atomic_compare_exchange\\(/__atomic_compare_exchange_db(/' src/dbinc/atomic.h && \ + sed -i.old 's/atomic_init/atomic_init_db/' src/dbinc/atomic.h src/mp/mp_region.c src/mp/mp_mvcc.c src/mp/mp_fget.c src/mutex/mut_method.c src/mutex/mut_tas.c endef else ifeq ($(host_os),mingw32) define $(package)_preprocess_cmds diff --git a/depends/packages/googletest.mk b/depends/packages/googletest.mk index 652e97aa..e35edf34 100644 --- a/depends/packages/googletest.mk +++ b/depends/packages/googletest.mk @@ -1,29 +1,47 @@ package=googletest -$(package)_version=1.7.0 +$(package)_version=1.8.1 $(package)_download_path=https://github.com/google/$(package)/archive/ $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_download_file=release-$($(package)_version).tar.gz -$(package)_sha256_hash=f73a6546fdf9fce9ff93a5015e0333a8af3062a152a9ad6bcb772c96687016cc +$(package)_sha256_hash=9bf1fe5182a604b4135edc1a425ae356c9ad15e9b23f9f12a02e80184c3a249c -ifeq ($(build_os),darwin) define $(package)_set_vars - $(package)_build_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" CXX="$($(package)_cxx)" CXXFLAGS="$($(package)_cxxflags)" +$(package)_cxxflags+=-std=c++11 +$(package)_cxxflags_linux=-fPIC endef + +ifeq ($(build_os),darwin) + define $(package)_set_vars + $(package)_build_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" CXX="$($(package)_cxx)" CXXFLAGS="$($(package)_cxxflags)" + endef endif ifeq ($(build_os),darwin) -$(package)_install=ginstall -define $(package)_build_cmds - $(MAKE) -C make gtest.a -endef + $(package)_install=ginstall + define $(package)_build_cmds + $(MAKE) -C googlemock/make gmock.a && \ + $(MAKE) -C googletest/make gtest.a + endef else -$(package)_install=install -define $(package)_build_cmds - $(MAKE) -C make CXXFLAGS=-fPIC gtest.a -endef + $(package)_install=install + + ifeq ($(build_os)$(host_os),linuxdarwin) # cross-compile for MacOS from Linux + define $(package)_build_cmds + $(MAKE) -C googlemock/make CC="$(build_prefix)/bin/$($(package)_cc)" CXX="$(build_prefix)/bin/$($(package)_cxx)" AR="$(build_prefix)/bin/$($(package)_ar)" CXXFLAGS="$($(package)_cxxflags)" gmock.a && \ + $(MAKE) -C googletest/make CC="$(build_prefix)/bin/$($(package)_cc)" CXX="$(build_prefix)/bin/$($(package)_cxx)" AR="$(build_prefix)/bin/$($(package)_ar)" CXXFLAGS="$($(package)_cxxflags)" gtest.a + endef + else + define $(package)_build_cmds + $(MAKE) -C googlemock/make CC="$($(package)_cc)" CXX="$($(package)_cxx)" AR="$($(package)_ar)" CXXFLAGS="$($(package)_cxxflags)" gmock.a && \ + $(MAKE) -C googletest/make CC="$($(package)_cc)" CXX="$($(package)_cxx)" AR="$($(package)_ar)" CXXFLAGS="$($(package)_cxxflags)" gtest.a + endef + endif endif define $(package)_stage_cmds - $($(package)_install) -D ./make/gtest.a $($(package)_staging_dir)$(host_prefix)/lib/libgtest.a && \ - cp -a ./include $($(package)_staging_dir)$(host_prefix)/include + mkdir -p $($(package)_staging_dir)$(host_prefix)/lib && \ + install ./googlemock/make/gmock.a $($(package)_staging_dir)$(host_prefix)/lib/libgmock.a && \ + install ./googletest/make/gtest.a $($(package)_staging_dir)$(host_prefix)/lib/libgtest.a && \ + cp -a ./googlemock/include $($(package)_staging_dir)$(host_prefix)/ && \ + cp -a ./googletest/include $($(package)_staging_dir)$(host_prefix)/ endef diff --git a/depends/packages/libcurl.mk b/depends/packages/libcurl.mk index 15981713..001173e2 100644 --- a/depends/packages/libcurl.mk +++ b/depends/packages/libcurl.mk @@ -1,46 +1,68 @@ package=libcurl -$(package)_version=7.64.1 +$(package)_version=7.67.0 $(package)_dependencies=openssl $(package)_download_path=https://curl.haxx.se/download $(package)_file_name=curl-$($(package)_version).tar.gz -$(package)_sha256_hash=432d3f466644b9416bc5b649d344116a753aeaa520c8beaf024a90cba9d3d35d +$(package)_sha256_hash=52af3361cf806330b88b4fe6f483b6844209d47ae196ac46da4de59bb361ab02 $(package)_config_opts_linux=--disable-shared --enable-static --prefix=$(host_prefix) --host=x86_64-unknown-linux-gnu $(package)_config_opts_mingw32=--enable-mingw --disable-shared --enable-static --prefix=$(host_prefix) --host=x86_64-w64-mingw32 $(package)_config_opts_darwin=--disable-shared --enable-static --prefix=$(host_prefix) -$(package)_cflags_darwin=-mmacosx-version-min=10.9 +$(package)_cflags_darwin=-mmacosx-version-min=$(OSX_MIN_VERSION) $(package)_conf_tool=./configure ifeq ($(build_os),darwin) -define $(package)_set_vars - $(package)_build_env=MACOSX_DEPLOYMENT_TARGET="10.9" -endef + define $(package)_set_vars + $(package)_build_env=MACOSX_DEPLOYMENT_TARGET="$(OSX_MIN_VERSION)" + endef endif ifeq ($(build_os),linux) -define $(package)_set_vars - $(package)_config_env=LD_LIBRARY_PATH="$(host_prefix)/lib" PKG_CONFIG_LIBDIR="$(host_prefix)/lib/pkgconfig" CPPFLAGS="-I$(host_prefix)/include" LDFLAGS="-L$(host_prefix)/lib" -endef + ifneq ($(host_os),darwin) + # linux build + define $(package)_set_vars + $(package)_config_env=LD_LIBRARY_PATH="$(host_prefix)/lib" PKG_CONFIG_LIBDIR="$(host_prefix)/lib/pkgconfig" CPPFLAGS="-I$(host_prefix)/include" LDFLAGS="-L$(host_prefix)/lib" + endef + else + # cross-compile for darwin + define $(package)_set_vars + $(package)_config_env+=LD_LIBRARY_PATH="$(host_prefix)/lib" PKG_CONFIG_LIBDIR="$(host_prefix)/lib/pkgconfig" + $(package)_config_env+=CPPFLAGS="-I$(host_prefix)/include -fPIC" LDFLAGS="-L$(host_prefix)/lib -Wl,-undefined -Wl,dynamic_lookup" # https://github.com/ddnet/ddnet/commit/e8bd8459a6f556594f48f33f4d145033bc89d46f + $(package)_config_env+=CC="$(build_prefix)/bin/$($(package)_cc)" CXX="$(build_prefix)/bin/$($(package)_cxx)" + $(package)_config_env+=AR="$(build_prefix)/bin/$($(package)_ar)" RANLIB="$(build_prefix)/bin/$($(package)_ranlib)" + $(package)_config_opts+=--host=$(canonical_host) --without-libpsl --disable-ldap --disable-tls-srp + endef + endif endif - -define $(package)_config_cmds - echo '=== config for $(package):' && \ - echo '$($(package)_config_env) $($(package)_conf_tool) $($(package)_config_opts)' && \ - echo '=== ' && \ - $($(package)_config_env) $($(package)_conf_tool) $($(package)_config_opts) -endef +ifeq ($(build_os)$(host_os),linuxdarwin) # cross-compile for MacOS from Linux + define $(package)_config_cmds + echo '=== config for $(package):' && \ + echo '$($(package)_config_env) $($(package)_conf_tool) $($(package)_config_opts)' && \ + echo '=== ' && \ + $($(package)_config_env) $($(package)_conf_tool) $($(package)_config_opts); \ + sed -i.old "/.*HAVE_RAND_EGD.*/d" ./lib/curl_config.h; \ + sed -i.old "/.*HAVE_BUILTIN_AVAILABLE.*/d" ./lib/curl_config.h + endef +else + define $(package)_config_cmds + echo '=== config for $(package):' && \ + echo '$($(package)_config_env) $($(package)_conf_tool) $($(package)_config_opts)' && \ + echo '=== ' && \ + $($(package)_config_env) $($(package)_conf_tool) $($(package)_config_opts) + endef +endif ifeq ($(build_os),darwin) -define $(package)_build_cmds - $(MAKE) CPPFLAGS="-I$(host_prefix)/include -fPIC" CFLAGS='-mmacosx-version-min=10.9' -endef + define $(package)_build_cmds + $(MAKE) CPPFLAGS="-I$(host_prefix)/include -fPIC" CFLAGS="-mmacosx-version-min=$(OSX_MIN_VERSION)" + endef else -define $(package)_build_cmds - $(MAKE) -endef + define $(package)_build_cmds + $(MAKE) + endef endif define $(package)_stage_cmds - echo 'Staging dir: $($(package)_staging_dir)$(host_prefix)/' && \ - $(MAKE) DESTDIR=$($(package)_staging_dir) install + echo 'Staging dir: $($(package)_staging_dir)$(host_prefix)/' && \ + $(MAKE) DESTDIR=$($(package)_staging_dir) install endef diff --git a/depends/packages/libgmp.mk b/depends/packages/libgmp.mk index 6d948c2a..c6b5569d 100644 --- a/depends/packages/libgmp.mk +++ b/depends/packages/libgmp.mk @@ -1,7 +1,7 @@ package=libgmp ifeq ($(host_os),mingw32) -$(package)_download_path=https://github.com/joshuayabut/$(package)/archive +$(package)_download_path=https://github.com/ca333/$(package)/archive $(package)_file_name=$(package)-$($(package)_git_commit).tar.gz $(package)_download_file=$($(package)_git_commit).tar.gz $(package)_sha256_hash=193836c1acc9dc00fe2521205d7bbe1ba13263f6cbef6f02584bf6f8b34b108f diff --git a/depends/packages/librustzcash.mk b/depends/packages/librustzcash.mk index a47c757d..13248ebc 100644 --- a/depends/packages/librustzcash.mk +++ b/depends/packages/librustzcash.mk @@ -8,8 +8,16 @@ $(package)_git_commit=06da3b9ac8f278e5d4ae13088cf0a4c03d2c13f5 $(package)_dependencies=rust $(rust_crates) $(package)_patches=cargo.config 0001-Start-using-cargo-clippy-for-CI.patch remove-dev-dependencies.diff +$(package)_rust_target=$(if $(rust_rust_target_$(canonical_host)),$(rust_rust_target_$(canonical_host)),$(canonical_host)) + ifeq ($(host_os),mingw32) $(package)_library_file=target/x86_64-pc-windows-gnu/release/rustzcash.lib +else ifneq ($(canonical_host),$(build)) + ifeq ($(build_os)$(host_os),darwindarwin) + $(package)_library_file=target/release/librustzcash.a + else + $(package)_library_file=target/$($(package)_rust_target)/release/librustzcash.a + endif else $(package)_library_file=target/release/librustzcash.a endif @@ -17,6 +25,9 @@ endif define $(package)_set_vars $(package)_build_opts=--frozen --release $(package)_build_opts_mingw32=--target=x86_64-pc-windows-gnu +ifneq ($(build_os),darwin) +$(package)_build_opts_darwin=--target=x86_64-apple-darwin +endif endef define $(package)_preprocess_cmds @@ -26,8 +37,9 @@ define $(package)_preprocess_cmds cat $($(package)_patch_dir)/cargo.config | sed 's|CRATE_REGISTRY|$(host_prefix)/$(CRATE_REGISTRY)|' > .cargo/config endef +# $(host_prefix)/native/bin/cargo build --package librustzcash $($(package)_build_opts) define $(package)_build_cmds - cargo build --package librustzcash $($(package)_build_opts) + RUSTFLAGS="${RUSTFLAGS} -A unused_mut" cargo build --package librustzcash $($(package)_build_opts) endef define $(package)_stage_cmds diff --git a/depends/packages/libsnark.mk b/depends/packages/libsnark.mk index 9d886099..63ab9e10 100644 --- a/depends/packages/libsnark.mk +++ b/depends/packages/libsnark.mk @@ -1,4 +1,5 @@ package=libsnark + $(package)_version=0.1 $(package)_download_path=https://github.com/ca333/libsnark/releases/download/v$($(package)_version)-$($(package)_git_commit)/ $(package)_file_name=$(package)-$($(package)_git_commit).tar.gz @@ -9,24 +10,31 @@ $(package)_git_commit=3854b20c25e8bc567aab2b558dec84d45f4a3e73 $(package)_dependencies=libgmp libsodium ifeq ($(build_os),darwin) -define $(package)_set_vars - $(package)_build_env=CC="$($(package)_cc)" CXX="$($(package)_cxx)" - $(package)_build_env+=CXXFLAGS="$($(package)_cxxflags) -DBINARY_OUTPUT -DSTATICLIB -DNO_PT_COMPRESSION=1 " -endef -define $(package)_build_cmds - $(MAKE) lib DEPINST=$(host_prefix) CURVE=ALT_BN128 MULTICORE=1 NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT OPTFLAGS="-O2 -march=x86-64 -g " -endef + define $(package)_set_vars + $(package)_build_env=CC="$($(package)_cc)" CXX="$($(package)_cxx)" + $(package)_build_env+=CXXFLAGS="$($(package)_cxxflags) -DBINARY_OUTPUT -DSTATICLIB -DNO_PT_COMPRESSION=1 " + endef + define $(package)_build_cmds + $(MAKE) lib DEPINST=$(host_prefix) CURVE=ALT_BN128 MULTICORE=1 NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT OPTFLAGS="-O2 -march=x86-64 -g " + endef else ifeq ($(host_os),mingw32) -define $(package)_build_cmds - CXX="x86_64-w64-mingw32-g++-posix" CXXFLAGS="-DBINARY_OUTPUT -DPTW32_STATIC_LIB -DSTATICLIB -DNO_PT_COMPRESSION=1 -fopenmp" $(MAKE) lib DEPINST=$(host_prefix) CURVE=ALT_BN128 MULTICORE=1 NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT OPTFLAGS="-O2 -march=x86-64 -g " -endef + define $(package)_build_cmds + CXX="x86_64-w64-mingw32-g++-posix" CXXFLAGS="-DBINARY_OUTPUT -DPTW32_STATIC_LIB -DSTATICLIB -DNO_PT_COMPRESSION=1 -fopenmp" $(MAKE) lib DEPINST=$(host_prefix) CURVE=ALT_BN128 MULTICORE=1 NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT OPTFLAGS="-O2 -march=x86-64 -g " + endef else -define $(package)_build_cmds - CXXFLAGS="-fPIC -DBINARY_OUTPUT -DNO_PT_COMPRESSION=1" $(MAKE) lib DEPINST=$(host_prefix) CURVE=ALT_BN128 MULTICORE=1 NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT OPTFLAGS="-O2 -march=x86-64 -g " -endef + ifneq ($(host_os),darwin) + define $(package)_build_cmds + CXXFLAGS="-fPIC -DBINARY_OUTPUT -DNO_PT_COMPRESSION=1" $(MAKE) lib DEPINST=$(host_prefix) CURVE=ALT_BN128 MULTICORE=1 NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT OPTFLAGS="-O2 -march=x86-64 -g " + endef + else + # cross-compile mac + define $(package)_build_cmds + CC="$(build_prefix)/bin/$($(package)_cc)" CXX="$(build_prefix)/bin/$($(package)_cxx)" \ + CXXFLAGS="-fPIC -DBINARY_OUTPUT -DNO_PT_COMPRESSION=1" $(MAKE) lib DEPINST=$(host_prefix) CURVE=ALT_BN128 MULTICORE=1 NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT OPTFLAGS="-O2 -march=x86-64 -g " + endef + endif endif - define $(package)_stage_cmds $(MAKE) install STATIC=1 DEPINST=$(host_prefix) PREFIX=$($(package)_staging_dir)$(host_prefix) CURVE=ALT_BN128 NO_SUPERCOP=1 endef diff --git a/depends/packages/libsodium.mk b/depends/packages/libsodium.mk index 4974edc7..fe8b0a4e 100644 --- a/depends/packages/libsodium.mk +++ b/depends/packages/libsodium.mk @@ -9,7 +9,7 @@ $(package)_config_opts= define $(package)_set_vars $(package)_build_env=DO_NOT_UPDATE_CONFIG_SCRIPTS=1 ifeq ($(build_os),darwin) - $(package)_build_env+=MACOSX_DEPLOYMENT_TARGET="10.11" + $(package)_build_env+=MACOSX_DEPLOYMENT_TARGET="$(OSX_MIN_VERSION)" $(package)_cc=clang $(package)_cxx=clang++ endif diff --git a/depends/packages/native_ccache.mk b/depends/packages/native_ccache.mk index ac2bb9f2..4c5dde76 100644 --- a/depends/packages/native_ccache.mk +++ b/depends/packages/native_ccache.mk @@ -1,8 +1,8 @@ package=native_ccache -$(package)_version=3.7.4 +$(package)_version=3.7.12 $(package)_download_path=https://github.com/ccache/ccache/releases/download/v$($(package)_version) -$(package)_file_name=ccache-$($(package)_version).tar.xz -$(package)_sha256_hash=04c0af414b8cf89e541daed59735547fbfd323b1aaa983da0216f6b6731e6836 +$(package)_file_name=ccache-$($(package)_version).tar.gz +$(package)_sha256_hash=d2abe88d4c283ce960e233583061127b156ffb027c6da3cf10770fc0c7244194 define $(package)_set_vars $(package)_config_opts= diff --git a/depends/packages/native_cctools.mk b/depends/packages/native_cctools.mk new file mode 100644 index 00000000..d56b6366 --- /dev/null +++ b/depends/packages/native_cctools.mk @@ -0,0 +1,116 @@ +package=native_cctools +$(package)_version=55562e4073dea0fbfd0b20e0bf69ffe6390c7f97 +$(package)_download_path=https://github.com/tpoechtrager/cctools-port/archive +$(package)_file_name=$($(package)_version).tar.gz +$(package)_sha256_hash=e51995a843533a3dac155dd0c71362dd471597a2d23f13dff194c6285362f875 +$(package)_build_subdir=cctools +$(package)_patches=ld64_disable_threading.patch + +ifeq ($(strip $(FORCE_USE_SYSTEM_CLANG)),) +$(package)_clang_version=8.0.0 +$(package)_clang_download_path=https://releases.llvm.org/$($(package)_clang_version) +$(package)_clang_download_file=clang+llvm-$($(package)_clang_version)-x86_64-linux-gnu-ubuntu-14.04.tar.xz +$(package)_clang_file_name=clang-llvm-$($(package)_clang_version)-x86_64-linux-gnu-ubuntu-14.04.tar.xz +$(package)_clang_sha256_hash=9ef854b71949f825362a119bf2597f744836cb571131ae6b721cd102ffea8cd0 +endif + +$(package)_libtapi_version=3efb201881e7a76a21e0554906cf306432539cef +$(package)_libtapi_download_path=https://github.com/tpoechtrager/apple-libtapi/archive +$(package)_libtapi_download_file=$($(package)_libtapi_version).tar.gz +$(package)_libtapi_file_name=$($(package)_libtapi_version).tar.gz +$(package)_libtapi_sha256_hash=380c1ca37cfa04a8699d0887a8d3ee1ad27f3d08baba78887c73b09485c0fbd3 + +$(package)_extra_sources=$($(package)_libtapi_file_name) +ifeq ($(strip $(FORCE_USE_SYSTEM_CLANG)),) +$(package)_extra_sources += $($(package)_clang_file_name) +endif + +ifeq ($(strip $(FORCE_USE_SYSTEM_CLANG)),) +define $(package)_fetch_cmds +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_download_file),$($(package)_file_name),$($(package)_sha256_hash)) && \ +$(call fetch_file,$(package),$($(package)_clang_download_path),$($(package)_clang_download_file),$($(package)_clang_file_name),$($(package)_clang_sha256_hash)) && \ +$(call fetch_file,$(package),$($(package)_libtapi_download_path),$($(package)_libtapi_download_file),$($(package)_libtapi_file_name),$($(package)_libtapi_sha256_hash)) +endef +else +define $(package)_fetch_cmds +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_download_file),$($(package)_file_name),$($(package)_sha256_hash)) && \ +$(call fetch_file,$(package),$($(package)_libtapi_download_path),$($(package)_libtapi_download_file),$($(package)_libtapi_file_name),$($(package)_libtapi_sha256_hash)) +endef +endif + +ifeq ($(strip $(FORCE_USE_SYSTEM_CLANG)),) +define $(package)_extract_cmds + mkdir -p $($(package)_extract_dir) && \ + echo "$($(package)_sha256_hash) $($(package)_source)" > $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + echo "$($(package)_clang_sha256_hash) $($(package)_source_dir)/$($(package)_clang_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + echo "$($(package)_libtapi_sha256_hash) $($(package)_source_dir)/$($(package)_libtapi_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + $(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + mkdir -p toolchain/bin toolchain/lib/clang/$($(package)_clang_version)/include && \ + mkdir -p libtapi && \ + tar --no-same-owner --strip-components=1 -C libtapi -xf $($(package)_source_dir)/$($(package)_libtapi_file_name) && \ + tar --no-same-owner --strip-components=1 -C toolchain -xf $($(package)_source_dir)/$($(package)_clang_file_name) && \ + rm -f toolchain/lib/libc++abi.so* && \ + tar --no-same-owner --strip-components=1 -xf $($(package)_source) +endef +else +define $(package)_extract_cmds + mkdir -p $($(package)_extract_dir) && \ + echo "$($(package)_sha256_hash) $($(package)_source)" > $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + echo "$($(package)_libtapi_sha256_hash) $($(package)_source_dir)/$($(package)_libtapi_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + $(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + mkdir -p libtapi && \ + tar --no-same-owner --strip-components=1 -C libtapi -xf $($(package)_source_dir)/$($(package)_libtapi_file_name) && \ + tar --no-same-owner --strip-components=1 -xf $($(package)_source) +endef +endif + +define $(package)_set_vars + $(package)_config_opts=--target=$(host) --with-libtapi=$($(package)_extract_dir) + $(package)_ldflags+=-Wl,-rpath=\\$$$$$$$$\$$$$$$$$ORIGIN/../lib + ifeq ($(strip $(FORCE_USE_SYSTEM_CLANG)),) + $(package)_config_opts+=--enable-lto-support --with-llvm-config=$($(package)_extract_dir)/toolchain/bin/llvm-config + $(package)_cc=$($(package)_extract_dir)/toolchain/bin/clang + $(package)_cxx=$($(package)_extract_dir)/toolchain/bin/clang++ + else + $(package)_cc=clang + $(package)_cxx=clang++ + endif +endef + +define $(package)_preprocess_cmds + CC=$($(package)_cc) CXX=$($(package)_cxx) INSTALLPREFIX=$($(package)_extract_dir) ./libtapi/build.sh && \ + CC=$($(package)_cc) CXX=$($(package)_cxx) INSTALLPREFIX=$($(package)_extract_dir) ./libtapi/install.sh && \ + patch -p1 < $($(package)_patch_dir)/ld64_disable_threading.patch +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +ifeq ($(strip $(FORCE_USE_SYSTEM_CLANG)),) +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install && \ + mkdir -p $($(package)_staging_prefix_dir)/lib/ && \ + cd $($(package)_extract_dir) && \ + cp lib/libtapi.so.6 $($(package)_staging_prefix_dir)/lib/ && \ + cd $($(package)_extract_dir)/toolchain && \ + mkdir -p $($(package)_staging_prefix_dir)/lib/clang/$($(package)_clang_version)/include && \ + mkdir -p $($(package)_staging_prefix_dir)/bin $($(package)_staging_prefix_dir)/include && \ + cp bin/clang $($(package)_staging_prefix_dir)/bin/ &&\ + cp -P bin/clang++ $($(package)_staging_prefix_dir)/bin/ &&\ + cp lib/libLTO.so $($(package)_staging_prefix_dir)/lib/ && \ + cp -rf lib/clang/$($(package)_clang_version)/include/* $($(package)_staging_prefix_dir)/lib/clang/$($(package)_clang_version)/include/ && \ + cp bin/dsymutil $($(package)_staging_prefix_dir)/bin/$(host)-dsymutil +endef +else +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install && \ + mkdir -p $($(package)_staging_prefix_dir)/lib/ && \ + cd $($(package)_extract_dir) && \ + cp lib/libtapi.so.6 $($(package)_staging_prefix_dir)/lib/ +endef +endif diff --git a/depends/packages/native_cdrkit.mk b/depends/packages/native_cdrkit.mk new file mode 100644 index 00000000..7bdf2d7d --- /dev/null +++ b/depends/packages/native_cdrkit.mk @@ -0,0 +1,28 @@ +package=native_cdrkit +$(package)_version=1.1.11 +$(package)_download_path=https://distro.ibiblio.org/fatdog/source/600/c +$(package)_file_name=cdrkit-$($(package)_version).tar.bz2 +$(package)_sha256_hash=b50d64c214a65b1a79afe3a964c691931a4233e2ba605d793eb85d0ac3652564 +$(package)_patches=cdrkit-deterministic.patch + +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/cdrkit-deterministic.patch +endef + +# Starting with 10.1, GCC defaults to -fno-common, resulting in linking errors. +# Pass -fcommon to retain the legacy behaviour. +define $(package)_config_cmds + $($(package)_cmake) -DCMAKE_C_FLAGS="$$($(1)_cflags) -fcommon" +endef + +define $(package)_build_cmds + $(MAKE) genisoimage +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) -C genisoimage install +endef + +define $(package)_postprocess_cmds + rm bin/isovfy bin/isoinfo bin/isodump bin/isodebug bin/devdump +endef diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index e64f1d82..341a30cd 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -50,17 +50,17 @@ rust_crates := \ crate_winapi_i686_pc_windows_gnu \ crate_winapi \ crate_winapi_x86_64_pc_windows_gnu + rust_packages := rust $(rust_crates) librustzcash native_packages := native_ccache wallet_packages=bdb -ifeq ($(host_os),linux) - packages := boost openssl libevent zeromq $(zcash_packages) googletest googlemock libcurl -else - packages := boost openssl libevent zeromq $(zcash_packages) googletest googlemock libcurl -endif - -native_packages := native_ccache +packages := boost openssl libevent zeromq $(zcash_packages) googletest libcurl # googlemock -wallet_packages=bdb +ifneq ($(build_os),darwin) +darwin_native_packages += native_cctools native_cdrkit +ifeq ($(host_os),darwin) +packages += libsnark +endif +endif diff --git a/depends/packages/rust.mk b/depends/packages/rust.mk index a08ac274..6057ccf4 100644 --- a/depends/packages/rust.mk +++ b/depends/packages/rust.mk @@ -1,52 +1,63 @@ package=rust -$(package)_version=1.28.0 +$(package)_version=1.36.0 $(package)_download_path=https://static.rust-lang.org/dist - $(package)_file_name_linux=rust-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz -$(package)_sha256_hash_linux=2a1390340db1d24a9498036884e6b2748e9b4b057fc5219694e298bdaa37b810 +$(package)_sha256_hash_linux=15e592ec52f14a0586dcebc87a957e472c4544e07359314f6354e2b8bd284c55 $(package)_file_name_darwin=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz -$(package)_sha256_hash_darwin=5d7a70ed4701fe9410041c1eea025c95cad97e5b3d8acc46426f9ac4f9f02393 -$(package)_file_name_mingw32=rust-$($(package)_version)-x86_64-pc-windows-gnu.tar.gz -$(package)_sha256_hash_mingw32=55c07426f791c51c8a2b6934b35784175c4abb4e03f123f3e847109c4dc1ad8b +$(package)_sha256_hash_darwin=91f151ec7e24f5b0645948d439fc25172ec4012f0584dd16c3fb1acb709aa325 +$(package)_file_name_freebsd=rust-$($(package)_version)-x86_64-unknown-freebsd.tar.gz +$(package)_sha256_hash_freebsd=eeeb1e9d0d7823c55f00f434789696e7249f465ba5966a5ab479040e3912c0e7 -ifeq ($(build_os),darwin) -$(package)_file_name=$($(package)_file_name_darwin) -$(package)_sha256_hash=$($(package)_sha256_hash_darwin) -else ifeq ($(host_os),mingw32) -$(package)_file_name=$($(package)_file_name_mingw32) -$(package)_sha256_hash=$($(package)_sha256_hash_mingw32) -else -$(package)_file_name=$($(package)_file_name_linux) -$(package)_sha256_hash=$($(package)_sha256_hash_linux) -endif +# Mapping from GCC canonical hosts to Rust targets +# If a mapping is not present, we assume they are identical +$(package)_rust_target_x86_64-apple-darwin11=x86_64-apple-darwin +$(package)_rust_target_x86_64-apple-darwin18=x86_64-apple-darwin +$(package)_rust_target_x86_64-w64-mingw32=x86_64-pc-windows-gnu + +# Mapping from Rust targets to SHA-256 hashes +$(package)_rust_std_sha256_hash_aarch64-unknown-linux-gnu=22bfc32b5003c3d5259babb202f3f66be16fa6f3c75c20f429a16d7ef5eb1928 +$(package)_rust_std_sha256_hash_x86_64-apple-darwin=7c6806809e010e5fba1780007ecff5c31f0ad2fcac1b414b98ca3baa0fb41b36 +$(package)_rust_std_sha256_hash_x86_64-pc-windows-gnu=3657c361f5d6048c2cb814f1773fece07b840276d60f012d0bf70993fa95a77e -ifeq ($(host_os),mingw32) +define rust_target +$(if $($(1)_rust_target_$(2)),$($(1)_rust_target_$(2)),$(if $(findstring darwin,$(3)),x86_64-apple-darwin,$(if $(findstring freebsd,$(3)),x86_64-unknown-freebsd,$(2)))) +endef + +ifneq ($(canonical_host),$(build)) +$(package)_rust_target=$(call rust_target,$(package),$(canonical_host),$(host_os)) +$(package)_exact_file_name=rust-std-$($(package)_version)-$($(package)_rust_target).tar.gz +$(package)_exact_sha256_hash=$($(package)_rust_std_sha256_hash_$($(package)_rust_target)) $(package)_build_subdir=buildos -$(package)_extra_sources = $($(package)_file_name_$(build_os)) +$(package)_extra_sources=$($(package)_file_name_$(build_os)) define $(package)_fetch_cmds $(call fetch_file,$(package),$($(package)_download_path),$($(package)_download_file),$($(package)_file_name),$($(package)_sha256_hash)) && \ $(call fetch_file,$(package),$($(package)_download_path),$($(package)_file_name_$(build_os)),$($(package)_file_name_$(build_os)),$($(package)_sha256_hash_$(build_os))) endef +# Runs from: build dir define $(package)_extract_cmds mkdir -p $($(package)_extract_dir) && \ echo "$($(package)_sha256_hash) $($(package)_source)" > $($(package)_extract_dir)/.$($(package)_file_name).hash && \ echo "$($(package)_sha256_hash_$(build_os)) $($(package)_source_dir)/$($(package)_file_name_$(build_os))" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ $(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \ - mkdir mingw32 && \ - tar --strip-components=1 -xf $($(package)_source) -C mingw32 && \ + mkdir $(canonical_host) && \ + tar --strip-components=1 -xf $($(package)_source) -C $(canonical_host) && \ mkdir buildos && \ - tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_file_name_$(build_os)) -C buildos + tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_file_name_$(build_os)) -C buildos && \ + echo "$(package) for $(build): $($(package)_staging_dir)/buildos" && \ + echo "$(package) for $(canonical_host): $($(package)_staging_dir)/$(canonical_host)" endef +# Runs from: build dir/$(package)_build_subdir +# first install.sh is from buildos for native build_os platform, second it for host_os (target) platform define $(package)_stage_cmds - ./install.sh --destdir=$($(package)_staging_dir) --prefix=$(host_prefix)/native --disable-ldconfig && \ - cp -r ../mingw32/rust-std-x86_64-pc-windows-gnu/lib/rustlib/x86_64-pc-windows-gnu $($(package)_staging_dir)$(host_prefix)/native/lib/rustlib + bash ./install.sh --destdir=$($(package)_staging_dir) --prefix=$(build_prefix) --disable-ldconfig && \ + ../$(canonical_host)/install.sh --destdir=$($(package)_staging_dir) --prefix=$(build_prefix) --disable-ldconfig endef else - +# for normal compilation (non-cross) define $(package)_stage_cmds - ./install.sh --destdir=$($(package)_staging_dir) --prefix=$(host_prefix)/native --disable-ldconfig + bash ./install.sh --destdir=$($(package)_staging_dir) --prefix=$(build_prefix) --disable-ldconfig endef endif diff --git a/depends/patches/native_cctools/ld64_disable_threading.patch b/depends/patches/native_cctools/ld64_disable_threading.patch new file mode 100644 index 00000000..d6c58c10 --- /dev/null +++ b/depends/patches/native_cctools/ld64_disable_threading.patch @@ -0,0 +1,26 @@ +commit 584668415039adeed073decee7e04de28248afd3 +Author: fanquake +Date: Tue Aug 18 01:20:24 2020 +0000 + + Disable threading to fix non-determinism + + A bug in the file parser can cause dependencies to be calculated + differently based on which files have already been parsed. This is more + likely to occur on systems with more CPUs. + + Just disable threading for now. There is no noticable slowdown. + + See #9891. + +diff --git a/cctools/ld64/src/ld/InputFiles.h b/cctools/ld64/src/ld/InputFiles.h +index ef9c756..90a70b6 100644 +--- a/cctools/ld64/src/ld/InputFiles.h ++++ b/cctools/ld64/src/ld/InputFiles.h +@@ -25,7 +25,6 @@ + #ifndef __INPUT_FILES_H__ + #define __INPUT_FILES_H__ + +-#define HAVE_PTHREADS 1 + + #include + #include diff --git a/depends/patches/native_cdrkit/cdrkit-deterministic.patch b/depends/patches/native_cdrkit/cdrkit-deterministic.patch new file mode 100644 index 00000000..8ab0993d --- /dev/null +++ b/depends/patches/native_cdrkit/cdrkit-deterministic.patch @@ -0,0 +1,86 @@ +--- cdrkit-1.1.11.old/genisoimage/tree.c 2008-10-21 19:57:47.000000000 -0400 ++++ cdrkit-1.1.11/genisoimage/tree.c 2013-12-06 00:23:18.489622668 -0500 +@@ -1139,8 +1139,9 @@ + scan_directory_tree(struct directory *this_dir, char *path, + struct directory_entry *de) + { +- DIR *current_dir; ++ int current_file; + char whole_path[PATH_MAX]; ++ struct dirent **d_list; + struct dirent *d_entry; + struct directory *parent; + int dflag; +@@ -1164,7 +1165,8 @@ + this_dir->dir_flags |= DIR_WAS_SCANNED; + + errno = 0; /* Paranoia */ +- current_dir = opendir(path); ++ //current_dir = opendir(path); ++ current_file = scandir(path, &d_list, NULL, alphasort); + d_entry = NULL; + + /* +@@ -1173,12 +1175,12 @@ + */ + old_path = path; + +- if (current_dir) { ++ if (current_file >= 0) { + errno = 0; +- d_entry = readdir(current_dir); ++ d_entry = d_list[0]; + } + +- if (!current_dir || !d_entry) { ++ if (current_file < 0 || !d_entry) { + int ret = 1; + + #ifdef USE_LIBSCHILY +@@ -1191,8 +1193,8 @@ + de->isorec.flags[0] &= ~ISO_DIRECTORY; + ret = 0; + } +- if (current_dir) +- closedir(current_dir); ++ if(d_list) ++ free(d_list); + return (ret); + } + #ifdef ABORT_DEEP_ISO_ONLY +@@ -1208,7 +1210,7 @@ + errmsgno(EX_BAD, "use Rock Ridge extensions via -R or -r,\n"); + errmsgno(EX_BAD, "or allow deep ISO9660 directory nesting via -D.\n"); + } +- closedir(current_dir); ++ free(d_list); + return (1); + } + #endif +@@ -1250,13 +1252,13 @@ + * The first time through, skip this, since we already asked + * for the first entry when we opened the directory. + */ +- if (dflag) +- d_entry = readdir(current_dir); ++ if (dflag && current_file >= 0) ++ d_entry = d_list[current_file]; + dflag++; + +- if (!d_entry) ++ if (current_file < 0) + break; +- ++ current_file--; + /* OK, got a valid entry */ + + /* If we do not want all files, then pitch the backups. */ +@@ -1348,7 +1350,7 @@ + insert_file_entry(this_dir, whole_path, d_entry->d_name); + #endif /* APPLE_HYB */ + } +- closedir(current_dir); ++ free(d_list); + + #ifdef APPLE_HYB + /* diff --git a/patches/correct_usage_libcurl.diff b/patches/correct_usage_libcurl.diff new file mode 100644 index 00000000..20f18c33 --- /dev/null +++ b/patches/correct_usage_libcurl.diff @@ -0,0 +1,118 @@ +diff --git a/configure.ac b/configure.ac +index b0184e9..3e4f4e4 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -853,6 +853,7 @@ if test x$use_pkgconfig = xyes; then + [ + PKG_CHECK_MODULES([SSL], [libssl],, [AC_MSG_ERROR(openssl not found.)]) + PKG_CHECK_MODULES([CRYPTO], [libcrypto],,[AC_MSG_ERROR(libcrypto not found.)]) ++ PKG_CHECK_MODULES([CURL], [libcurl],,[AC_MSG_ERROR(libcurl not found.)]) + if test x$enable_bip70 != xno; then + BITCOIN_QT_CHECK([PKG_CHECK_MODULES([PROTOBUF], [protobuf], [have_protobuf=yes], [BITCOIN_QT_FAIL(libprotobuf not found)])]) + fi +@@ -887,6 +888,9 @@ else + AC_CHECK_HEADER([openssl/ssl.h],, AC_MSG_ERROR(libssl headers missing),) + AC_CHECK_LIB([ssl], [main],SSL_LIBS=-lssl, AC_MSG_ERROR(libssl missing)) + ++ AC_CHECK_HEADER([curl/curl.h],, AC_MSG_ERROR(libcurl headers missing),) ++ AC_CHECK_LIB([curl], [curl_global_init],[CURL_LIBS=-lcurl], AC_MSG_ERROR([unable to link with libcurl]),[-lssl -lcrypto]) ++ + if test x$build_komodo_utils$build_komodod$bitcoin_enable_qt$use_tests != xnononono; then + AC_CHECK_HEADER([event2/event.h],, AC_MSG_ERROR(libevent headers missing),) + AC_CHECK_LIB([event],[main],EVENT_LIBS=-levent,AC_MSG_ERROR(libevent missing)) +@@ -1213,6 +1217,7 @@ AC_SUBST(ARM_CRC_CXXFLAGS) + AC_SUBST(LIBTOOL_APP_LDFLAGS) + AC_SUBST(USE_UPNP) + AC_SUBST(BOOST_LIBS) ++AC_SUBST(CURL_LIBS) + AC_SUBST(TESTDEFS) + AC_SUBST(LEVELDB_TARGET_FLAGS) + AC_SUBST(LEVELDB_ATOMIC_CPPFLAGS) +diff --git a/depends/packages/libcurl.mk b/depends/packages/libcurl.mk +index 001173e..a1d3e21 100644 +--- a/depends/packages/libcurl.mk ++++ b/depends/packages/libcurl.mk +@@ -5,7 +5,7 @@ $(package)_download_path=https://curl.haxx.se/download + $(package)_file_name=curl-$($(package)_version).tar.gz + $(package)_sha256_hash=52af3361cf806330b88b4fe6f483b6844209d47ae196ac46da4de59bb361ab02 + $(package)_config_opts_linux=--disable-shared --enable-static --prefix=$(host_prefix) --host=x86_64-unknown-linux-gnu +-$(package)_config_opts_mingw32=--enable-mingw --disable-shared --enable-static --prefix=$(host_prefix) --host=x86_64-w64-mingw32 ++$(package)_config_opts_mingw32=--enable-mingw --disable-shared --enable-static --prefix=$(host_prefix) --host=x86_64-w64-mingw32 --disable-ldap + $(package)_config_opts_darwin=--disable-shared --enable-static --prefix=$(host_prefix) + $(package)_cflags_darwin=-mmacosx-version-min=$(OSX_MIN_VERSION) + $(package)_conf_tool=./configure +diff --git a/src/Makefile.am b/src/Makefile.am +index 60b03df..939243b 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -17,15 +17,7 @@ BITCOIN_INCLUDES += -I$(srcdir)/snark + BITCOIN_INCLUDES += -I$(srcdir)/snark/libsnark + BITCOIN_INCLUDES += -I$(srcdir)/univalue/include + +-if TARGET_WINDOWS +-LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl +-endif +-if TARGET_DARWIN +-LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl +-else +-LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl +-endif +- ++LIBBITCOIN_SERVER=libbitcoin_server.a + LIBBITCOIN_WALLET=libbitcoin_wallet.a + LIBBITCOIN_COMMON=libbitcoin_common.a + LIBBITCOIN_CLI=libbitcoin_cli.a +@@ -50,7 +42,7 @@ LIBSECP256K1=secp256k1/libsecp256k1.la + LIBCRYPTOCONDITIONS=cryptoconditions/libcryptoconditions_core.la + LIBSNARK=snark/libsnark.a + LIBUNIVALUE=univalue/libunivalue.la +-LIBZCASH=libzcash.a -lcurl ++LIBZCASH=libzcash.a + + $(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) + $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) +diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include +index 30d8fcb..05d8a94 100644 +--- a/src/Makefile.qt.include ++++ b/src/Makefile.qt.include +@@ -446,39 +446,7 @@ if TARGET_WINDOWS + qt_komodo_qt_SOURCES += $(KOMODO_RC) + endif + +-# libbitcoin_server.a(libbitcoin_server_a-rpcserver.o): In function `__static_initialization_and_destruction_0(int, int)': +-# +-#/home/decker/ssd_m2/komodoocean_src/src/rpcserver.cpp:413: undefined reference to `kvupdate(UniValue const&, bool)' +-#/home/decker/ssd_m2/komodoocean_src/src/rpcserver.cpp:413: undefined reference to `fundrawtransaction(UniValue const&, bool)' +-#/home/decker/ssd_m2/komodoocean_src/src/rpcserver.cpp:413: undefined reference to `resendwallettransactions(UniValue const&, bool)' +-#/home/decker/ssd_m2/komodoocean_src/src/rpcserver.cpp:413: undefined reference to `addmultisigaddress(UniValue const&, bool)' +-#/home/decker/ssd_m2/komodoocean_src/src/rpcserver.cpp:413: undefined reference to `backupwallet(UniValue const&, bool)' +-#/home/decker/ssd_m2/komodoocean_src/src/rpcserver.cpp:413: undefined reference to `dumpprivkey(UniValue const&, bool)' +-#/home/decker/ssd_m2/komodoocean_src/src/rpcserver.cpp:413: undefined reference to `dumpwallet(UniValue const&, bool)' +-#/home/decker/ssd_m2/komodoocean_src/src/rpcserver.cpp:413: undefined reference to `encryptwallet(UniValue const&, bool)' +-# +-# we need add libbitcoin_server_a-rpcserver.o wallet/libbitcoin_wallet_a-rpcwallet.o +-# +-# Objects? +-# wallet/libbitcoin_wallet_a-rpcdump.$(OBJEXT) \ +-# wallet/libbitcoin_wallet_a-rpcwallet.$(OBJEXT) \ +-# Or sources? +-# wallet/rpcdump.cpp \ +-# wallet/rpcwallet.cpp \ +-# +-# Link of komodod: +-# ---------------- +-# komodo_qt_clean: FORCE +-# rm -f $(CLEAN_QT) $(qt_libkomodoqt_a_OBJECTS) $(qt_komodo_qt_OBJECTS) qt/komodo-qt$(EXEEXT) $(LIBBITCOINQT) +-# +-# komodo_qt : qt/komodo-qt$(EXEEXT) +-# +-# From written makefile: +-# komodod$(EXEEXT): $(komodod_OBJECTS) $(komodod_DEPENDENCIES) $(EXTRA_komodod_DEPENDENCIES) +-# @rm -f komodod$(EXEEXT) +-# $(AM_V_CXXLD)$(komodod_LINK) $(komodod_OBJECTS) $(komodod_LDADD) $(LIBS) +- +-qt_komodo_qt_LDADD = $(LIBBITCOIN_SERVER) qt/libkomodoqt.a ++qt_komodo_qt_LDADD = $(LIBBITCOIN_SERVER) $(CURL_LIBS) qt/libkomodoqt.a + if ENABLE_ZMQ + qt_komodo_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) + endif diff --git a/patches/glibc_compat_patch.diff b/patches/glibc_compat_patch.diff new file mode 100644 index 00000000..8183749e --- /dev/null +++ b/patches/glibc_compat_patch.diff @@ -0,0 +1,134 @@ +diff --git a/configure.ac b/configure.ac +index 0359d1c..38c2cb1 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -568,6 +568,9 @@ AX_GCC_FUNC_ATTRIBUTE([dllimport]) + + if test x$use_glibc_compat != xno; then + ++ AX_CHECK_LINK_FLAG([[-Wl,--wrap=__divmoddi4]], [COMPAT_LDFLAGS="$COMPAT_LDFLAGS -Wl,--wrap=__divmoddi4"]) ++ AX_CHECK_LINK_FLAG([[-Wl,--wrap=log2f]], [COMPAT_LDFLAGS="$COMPAT_LDFLAGS -Wl,--wrap=log2f"]) ++ + #glibc absorbed clock_gettime in 2.17. librt (its previous location) is safe to link + #in anyway for back-compat. + AC_CHECK_LIB([rt],[clock_gettime],, AC_MSG_ERROR(lib missing)) +@@ -1209,6 +1212,7 @@ AC_SUBST(BITCOIN_GUI_NAME) + AC_SUBST(BITCOIN_CLI_NAME) + AC_SUBST(BITCOIN_TX_NAME) + ++AC_SUBST(COMPAT_LDFLAGS) + AC_SUBST(RELDFLAGS) + AC_SUBST(SSE42_CXXFLAGS) + AC_SUBST(SSE41_CXXFLAGS) +diff --git a/src/Makefile.am b/src/Makefile.am +index 1cbf956..f825d77 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -529,7 +529,8 @@ libbitcoin_util_a_SOURCES = \ + $(LIBZCASH_H) + + if GLIBC_BACK_COMPAT +-libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp ++ libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp ++ AM_LDFLAGS += $(COMPAT_LDFLAGS) + endif + + # cli: zcash-cli +@@ -694,6 +695,7 @@ libzcashconsensus_la_SOURCES = \ + + if GLIBC_BACK_COMPAT + libzcashconsensus_la_SOURCES += compat/glibc_compat.cpp ++ AM_LDFLAGS += $(COMPAT_LDFLAGS) + endif + + libzcashconsensus_la_LDFLAGS = -no-undefined $(RELDFLAGS) -static +diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp +index 3b9c70d..d0c5ed4 100644 +--- a/src/compat/glibc_compat.cpp ++++ b/src/compat/glibc_compat.cpp +@@ -7,6 +7,7 @@ + #endif + + #include ++#include + + #if defined(HAVE_SYS_SELECT_H) + #include +@@ -27,3 +28,77 @@ extern "C" FDELT_TYPE __fdelt_warn(FDELT_TYPE a) + return a / __NFDBITS; + } + extern "C" FDELT_TYPE __fdelt_chk(FDELT_TYPE) __attribute__((weak, alias("__fdelt_warn"))); ++ ++#if defined(__i386__) || defined(__arm__) ++ ++extern "C" int64_t __udivmoddi4(uint64_t u, uint64_t v, uint64_t* rp); ++ ++extern "C" int64_t __wrap___divmoddi4(int64_t u, int64_t v, int64_t* rp) ++{ ++ int32_t c1 = 0, c2 = 0; ++ int64_t uu = u, vv = v; ++ int64_t w; ++ int64_t r; ++ ++ if (uu < 0) { ++ c1 = ~c1, c2 = ~c2, uu = -uu; ++ } ++ if (vv < 0) { ++ c1 = ~c1, vv = -vv; ++ } ++ ++ w = __udivmoddi4(uu, vv, (uint64_t*)&r); ++ if (c1) ++ w = -w; ++ if (c2) ++ r = -r; ++ ++ *rp = r; ++ return w; ++} ++#endif ++ ++extern "C" float log2f_old(float x); ++#ifdef __i386__ ++__asm(".symver log2f_old,log2f@GLIBC_2.1"); ++#elif defined(__amd64__) ++__asm(".symver log2f_old,log2f@GLIBC_2.2.5"); ++#elif defined(__arm__) ++__asm(".symver log2f_old,log2f@GLIBC_2.4"); ++#elif defined(__aarch64__) ++__asm(".symver log2f_old,log2f@GLIBC_2.17"); ++#elif defined(__riscv) ++__asm(".symver log2f_old,log2f@GLIBC_2.27"); ++#endif ++extern "C" float __wrap_log2f(float x) ++{ ++ return log2f_old(x); ++} ++ ++/* glibc-internal users use __explicit_bzero_chk, and explicit_bzero ++redirects to that. */ ++#undef explicit_bzero ++/* Set LEN bytes of S to 0. The compiler will not delete a call to ++this function, even if S is dead after the call. */ ++void explicit_bzero (void *s, size_t len) ++{ ++ memset (s, '\0', len); ++ /* Compiler barrier. */ ++ asm volatile ("" ::: "memory"); ++} ++ ++// Redefine explicit_bzero_chk ++void __explicit_bzero_chk (void *dst, size_t len, size_t dstlen) ++{ ++ /* Inline __memset_chk to avoid a PLT reference to __memset_chk. */ ++ if (__glibc_unlikely (dstlen < len)) ++ __chk_fail (); ++ memset (dst, '\0', len); ++ /* Compiler barrier. */ ++ asm volatile ("" ::: "memory"); ++} ++/* libc-internal references use the hidden ++__explicit_bzero_chk_internal symbol. This is necessary if ++__explicit_bzero_chk is implemented as an IFUNC because some ++targets do not support hidden references to IFUNC symbols. */ ++#define strong_alias (__explicit_bzero_chk, __explicit_bzero_chk_internal) diff --git a/patches/openssl_m1.patch b/patches/openssl_m1.patch new file mode 100644 index 00000000..603a2677 --- /dev/null +++ b/patches/openssl_m1.patch @@ -0,0 +1,23 @@ +diff --git a/depends/packages/openssl.mk b/depends/packages/openssl.mk +index 296a55389..80e2d9cf2 100644 +--- a/depends/packages/openssl.mk ++++ b/depends/packages/openssl.mk +@@ -1,8 +1,8 @@ + package=openssl +-$(package)_version=1.1.1f ++$(package)_version=1.1.1i + $(package)_download_path=https://www.openssl.org/source + $(package)_file_name=$(package)-$($(package)_version).tar.gz +-$(package)_sha256_hash=186c6bfe6ecfba7a5b48c47f8a1673d0f3b0e5ba2e25602dd23b629975da3f35 ++$(package)_sha256_hash=e8be6a35fe41d10603c3cc635e93289ed00bf34b79671a3a4de64fcee00d5242 + + define $(package)_set_vars + $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" +@@ -83,6 +83,7 @@ $(package)_config_opts_mipsel_linux=linux-generic32 + $(package)_config_opts_mips_linux=linux-generic32 + $(package)_config_opts_powerpc_linux=linux-generic32 + $(package)_config_opts_x86_64_darwin=darwin64-x86_64-cc ++$(package)_config_opts_arm_darwin=darwin64-arm64-cc + $(package)_config_opts_x86_64_mingw32=mingw64 + $(package)_config_opts_i686_mingw32=mingw + endef diff --git a/share/pixmaps/bitcoin.ico b/share/pixmaps/bitcoin.ico index f5480f41..33cfc44c 100644 Binary files a/share/pixmaps/bitcoin.ico and b/share/pixmaps/bitcoin.ico differ diff --git a/share/pixmaps/bitcoin128.png b/share/pixmaps/bitcoin128.png index 55039b1c..6655ea10 100644 Binary files a/share/pixmaps/bitcoin128.png and b/share/pixmaps/bitcoin128.png differ diff --git a/share/pixmaps/bitcoin128.xpm b/share/pixmaps/bitcoin128.xpm index d8e41e9e..6a3e39f5 100644 --- a/share/pixmaps/bitcoin128.xpm +++ b/share/pixmaps/bitcoin128.xpm @@ -1,384 +1,369 @@ /* XPM */ -static char *bitcoin___[] = { +static char *bitcoin128[] = { /* columns rows colors chars-per-pixel */ -"128 128 250 2", -" c #845415", -". c #895616", -"X c #84581E", -"o c #8D5C18", -"O c #925A15", -"+ c #925E1C", -"@ c #98621C", -"# c #9E711C", -"$ c #A36E1A", -"% c #A96F1B", -"& c #A6711C", -"* c #AC741C", -"= c #B2741E", -"- c #B37C1E", -"; c #BB7C1E", -": c #835B21", -"> c #8F6125", -", c #956727", -"< c #916B2E", -"1 c #996B2C", -"2 c #B47B23", -"3 c #BD7C20", -"4 c #A17330", -"5 c #AB7D3B", -"6 c #C17F20", -"7 c #B9831F", -"8 c #BB842B", -"9 c #BD8533", -"0 c #B68F3D", -"q c #BE8C3B", -"w c #C4801F", -"e c #FE8C03", -"r c #F38A0F", -"t c #FD8E0A", -"y c #FF910C", -"u c #F78F13", -"i c #F98F10", -"p c #F79016", -"a c #FE9314", -"s c #F6931E", -"d c #FD961B", -"f c #FE991E", -"g c #C58421", -"h c #CD8621", -"j c #C78B21", -"k c #CC8B23", -"l c #C2852B", -"z c #C08B2D", -"x c #D28722", -"c c #D38B25", -"v c #DB8E22", -"b c #D28E2C", -"n c #D49323", -"m c #DC9224", -"M c #DC9B25", -"N c #D4922D", -"B c #DF972A", -"V c #DF982E", -"C c #C18D33", -"Z c #C58E38", -"A c #CB9332", -"S c #C2933C", -"D c #CD9339", -"F c #CC9938", -"G c #D19733", -"H c #DA9230", -"J c #D59935", -"K c #DC9C33", -"L c #DC9E3B", -"P c #E49124", -"I c #EA9426", -"U c #E09D26", -"Y c #EC972B", -"T c #F79625", -"R c #F99524", -"E c #F69A26", -"W c #F89825", -"Q c #F2972B", -"! c #F59A2C", -"~ c #F89B2B", -"^ c #E79D33", -"/ c #EF9D31", -"( c #E19F3A", -") c #EF9D3A", -"_ c #F49C33", -"` c #F99E32", -"' c #F49F39", -"] c #D6A13E", -"[ c #DAA33B", -"{ c #E3A127", -"} c #E7A328", -"| c #EDA32C", -" . c #EDA829", -".. c #FFA325", -"X. c #FFAB25", -"o. c #F3A42B", -"O. c #FFA429", -"+. c #F4A929", -"@. c #FFAC2A", -"#. c #FFB227", -"$. c #FFB32C", -"%. c #FFBA2D", -"&. c #EEA830", -"*. c #F7A334", -"=. c #FAA036", -"-. c #FCAB34", -";. c #F4A13C", -":. c #F9A33B", -">. c #F4A83B", -",. c #FFA83F", -"<. c #FDB432", -"1. c #FFBB33", -"2. c #FFB73A", -"3. c #FDB93E", -"4. c #FFC12F", -"5. c #FFC432", -"6. c #FFC338", -"7. c #D2A043", -"8. c #D8A140", -"9. c #EEA144", -"0. c #E2A840", -"q. c #EDA34B", -"w. c #F4A444", -"e. c #F9A642", -"r. c #FBA945", -"t. c #F3A64B", -"y. c #F4A84E", -"u. c #FBAB4B", -"i. c #EEB041", -"p. c #FABA44", -"a. c #ECA653", -"s. c #EEAC5D", -"d. c #F3AA53", -"f. c #FAAE53", -"g. c #F2AD5A", -"h. c #FBB056", -"j. c #F6B15E", -"k. c #FBB25B", -"l. c #DDAF79", -"z. c #E3A962", -"x. c #EBAE63", -"c. c #E4AC68", -"v. c #EAAF69", -"b. c #EEB065", -"n. c #E7B06C", -"m. c #EEB36B", -"M. c #F5B263", -"N. c #FBB461", -"B. c #E6B274", -"V. c #ECB574", -"C. c #E7B57B", -"Z. c #EAB77C", -"A. c #ECB97C", -"S. c #F2B770", -"D. c #F0BB7A", -"F. c #DBB485", -"G. c #DFB888", -"H. c #E4B984", -"J. c #EDBD82", -"K. c #E5BC8B", -"L. c #EABE8A", -"P. c #F0BE82", -"I. c #E0BF96", -"U. c #EDC089", -"Y. c #F0C28B", -"T. c #E5C194", -"R. c #E9C191", -"E. c #E4C39C", -"W. c #EBC699", -"Q. c #EBC99F", -"!. c #DFC3A0", -"~. c #DDCAAF", -"^. c #CFC7BD", -"/. c #D2CBB6", -"(. c #DBC8B1", -"). c #DBCDBB", -"_. c #E2C6A4", -"`. c #E6C8A5", -"'. c #EACBA5", -"]. c #E1C7A8", -"[. c #E3CBAD", -"{. c #EACCAA", -"}. c #EED1AC", -"|. c #E1CDB3", -" X c #E3CFB8", -".X c #E6D1B6", -"XX c #EBD2B3", -"oX c #E3D1BB", -"OX c #EAD6BB", -"+X c #EBD8BF", -"@X c #D3CDC2", -"#X c #D8CDC2", -"$X c #D0CECA", -"%X c #DDD3C4", -"&X c #D3D2CC", -"*X c #DDD5CB", -"=X c #CCD3D5", -"-X c #C9D7DF", -";X c #D2D4D6", -":X c #DEDAD4", -">X c #DDDCDB", -",X c #E2D4C2", -" c #1D4B78", +", c #0C5375", +"< c #0B5C7A", +"1 c #055776", +"2 c #155376", +"3 c #195276", +"4 c #185679", +"5 c #175D7C", +"6 c #18597A", +"7 c #175678", +"8 c #2B5378", +"9 c #295177", +"0 c #04637E", +"q c #09627E", +"w c #16617F", +"e c #1A5E82", +"r c #026B83", +"t c #0C6C84", +"y c #0B6681", +"u c #166581", +"i c #196283", +"p c #156C85", +"a c #166E89", +"s c #196A87", +"d c #027287", +"f c #027589", +"g c #0C7489", +"h c #027C8D", +"j c #0C768A", +"k c #14738A", +"l c #127A8E", +"z c #16758B", +"x c #127E91", +"c c #1A7991", +"v c #0E7F90", +"b c #276584", +"n c #386787", +"m c #23748C", +"M c #3A7A93", +"N c #2A7A91", +"B c #385D81", +"V c #466C8B", +"C c #557895", +"Z c #4F7794", +"A c #00808F", +"S c #018492", +"D c #0C8393", +"F c #018A95", +"G c #0B8996", +"H c #0F8E9A", +"J c #018E98", +"K c #118394", +"L c #108997", +"P c #108B99", +"I c #188497", +"U c #02949B", +"Y c #0E939D", +"T c #019A9F", +"R c #10909D", +"E c #29899A", +"W c #338A9C", +"Q c #0E97A0", +"! c #029DA1", +"~ c #0D9BA2", +"^ c #0F9FA9", +"/ c #1198A4", +"( c #128FA0", +") c #2699A4", +"_ c #3B9AA7", +"` c #01A3A4", +"' c #0CA2A6", +"] c #0BA6A9", +"[ c #02ACAA", +"{ c #0BACAC", +"} c #01A8A7", +"| c #17AAAC", +" . c #01B2AE", +".. c #0AB1AF", +"X. c #0DAEB2", +"o. c #03B6B1", +"O. c #09B5B1", +"+. c #05BDB5", +"@. c #08BBB5", +"#. c #07BFB8", +"$. c #0BBBBA", +"%. c #13BAB6", +"&. c #1DAFB0", +"*. c #24B7B6", +"=. c #3BB9BA", +"-. c #30AAAF", +";. c #5C849D", +":. c #4E819A", +">. c #66829D", +",. c #558CA1", +"<. c #439BA8", +"1. c #579CAC", +"2. c #4E8FA2", +"3. c #688BA4", +"4. c #748EA6", +"5. c #6B94AA", +"6. c #7695AB", +"7. c #789DB0", +"8. c #48ABB3", +"9. c #59A5B2", +"0. c #58B3BB", +"q. c #4DAEB6", +"w. c #65AAB6", +"e. c #6AB3BC", +"r. c #77AEBB", +"t. c #01C1B6", +"y. c #06C4BA", +"u. c #0AC1BC", +"i. c #05CABE", +"p. c #0EC9BE", +"a. c #31C2BF", +"s. c #7BBAC3", +"d. c #6EBBC2", +"f. c #5CBCC1", +"g. c #05CEC1", +"h. c #09C8C1", +"j. c #19CDC2", +"k. c #04D3C4", +"l. c #02D9C7", +"z. c #03DDC9", +"x. c #08D6C9", +"c. c #13D4C6", +"v. c #2CCDC5", +"b. c #38C6C2", +"n. c #27DACC", +"m. c #34D9CD", +"M. c #01E3CD", +"N. c #00E9CE", +"B. c #02E6D1", +"V. c #00EDD3", +"C. c #09EED6", +"Z. c #1AEAD4", +"A. c #00F1D6", +"S. c #00F4DA", +"D. c #00F9DE", +"F. c #17E2CF", +"G. c #27ECD7", +"H. c #30EDD9", +"J. c #00FEE3", +"K. c #48C8C5", +"L. c #58C9C8", +"P. c #4AD1CB", +"I. c #57D1CC", +"U. c #46DDD2", +"Y. c #59DBD3", +"T. c #65C8C9", +"R. c #7AC6CA", +"E. c #64D1CE", +"W. c #68D8D3", +"Q. c #78D8D6", +"!. c #46EADA", +"~. c #4FEBDC", +"^. c #67E6DC", +"/. c #78E5DD", +"(. c #5AF1E1", +"). c #49F2E0", +"_. c #6AEDE1", +"`. c #7BECE2", +"'. c #68F2E3", +"]. c #78F4E7", +"[. c #88A6B9", +"{. c #92A8BB", +"}. c #8FB0BF", +"|. c #849EB3", +" X c #99B7C5", +".X c #8FB4C2", +"XX c #A4B8C7", +"oX c #88C7CD", +"OX c #98CAD1", +"+X c #87D4D4", +"@X c #9CD7D9", +"#X c #8FC9CF", +"$X c #ABC0CD", +"%X c #A7C9D2", +"&X c #B8C9D4", +"*X c #A5D8DB", +"=X c #BCD2DA", +"-X c #B7D5DB", +";X c #B2C0CE", +":X c #88E2DD", +">X c #B3DEE1", +",X c #86EDE4", +".N b b b b N >.( C > HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX4 L _ *.@.<.$.X.X...X.X.X.X.X.X...X.@.$.<.@.*./ G , HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX< L -.@.$.X...R R R T T T T W W W W W W T T T T R R W ..X.$.@.*.J HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXD -.%.X.W R T T W W W W W W W W W W W W W W W W W W W W W W T T R W X.%.+.A HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXS -.$.X.R T T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W T T R X.$.-.C HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXF <.@.f R T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W T R W #.<.A HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX[ <.X.f T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W T R X.$.K HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX0.$...R T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E W W W W W W W T R ..%.G HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXS 1...R T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ E W W W W W W W W W T R X.1.A HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX3.X.d T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ E W W W W W W W W W W T R @.2.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX7.5.f T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W T W %.z HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX3.X.s T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W T R $.<.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX1...R W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ E E W W W W W W W W W W W W W R ..1.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX0 5.f T W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W T W 5.8 HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX8.$.s W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W T R %.N HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXi.#.R W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W R $.&.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXp.X.R W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W R @.<.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXp.X.R W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W R @.<.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXi.X.R W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ E ~ W R ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` ` ` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W R @.| HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX] #.R W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ! s e t d ~ ` ` ` ` ` ` =.=.=.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W R %.N HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXq %.R W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E W E ~ ~ ~ ~ y l.=XI.x.) p a =.` ` =.=.=.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W R %.2 HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX5 5.d W W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ t (.jXVXNXuX@XF.W ` =.:.` W =.:.=.=.` ` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W T R 5.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX1.f T W W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ R Q eXDXSXSXDXgX#Xa ` =.=.;.q.W a a R ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W T W %.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX3...T W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ` a a.NXSXGXGXAXNXV.a :.:.f c.tX*XE.n.9.R ~ ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W T @.@.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXD #.R W W W W W W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` t H.VXSXGXGXDXmXy.f :.:.a I.hXBXCXNXiX^.' W ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W R %.g HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX5.d W W W W W W W W W W W W W W W W W W W W W W W W W E ~ W ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` i |.CXGXGXGXCX3X~ ` :.:.R %XCXSXGXAXNX>XW ~ ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W R 5.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX2.W T W W W W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ s t e a W ~ ` ` ` ` ` ` W ! eXFXGXGXSXVX[.d :.:.~ w.uXFXGXGXSXVXW.a ` ` ` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W T ..@.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHX9 $.R W W W W W W W W W W W W W W W W W W W W W W E W ~ ~ ~ y F./.B.9.T t t a ~ =.` =.a a.hXDXGXGXSXNXA.d :.e.R v.NXSXGXGXSXNXm.a =.` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W R %.= HXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXHX6.d W W W W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ W i &XjXNXfX:X].B.q.T t a d e K.VXSXGXGXDXaXd.W e.e.d E.VXSXGXGXDXvXw.W =.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W W W %.HXHXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHXK X.T W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ a ) uXDXSXFXFXCXNXfX:X_.B.q.r .XFXGXGXGXCX3X=.=.e.,.~ %XCXGXGXGXCX1XW ` =.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W T $.m HXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHXHX5.R W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ~ ~ t x.NXSXGXGXGXSXSXDXFXCXNXmX8XcXSXGXGXGXCXW.e :.e.=.t.uXFXGXGXSXVXE.d :.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W W R %.HXHXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHX^ X.T W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ ~ ~ ` t T.VXSXGXGXGXGXGXGXGXSXSXFXGXGXGXGXGXGXFX}.9.' W e v.VXSXGXGXSXNXm.d :.=.=.=.` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W W T @.P HXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXHX1.R W W W W W W W W W W W W W W W W E E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ s ;XNXAXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXFXNX>X|.V.XXFXGXGXGXFXbXy.~ :.:.=.=.` ` ` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W W R %.HXHXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHXH X.T W W W W W W W W W W W W W W E E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` R ' $XsXNXVXFXSXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXFXCXCXFXSXGXGXGXCXOXa :.:.:.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W W T $.c HXHXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXHX1.R W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` ~ t.V.`.5XVXFXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXFXXFXGXGXGXGXGXGXGXSXCX{.e.P.'.2XvXNXBXDXSXGXGXGXGXGXGXGXGXGXSXDXjX~.y W =.` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W W W @.HXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHX: 1.R W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` =.=.=.=.=.:.:.:.:.:.:.:.:.e.e.e.~ s.fXDXGXGXGXGXGXGXGXSXNXD.f =.=.,.M.L.oXaXVXDXSXGXGXGXGXGXGXGXGXGXAXVX(.t ~ ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W R %. HXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXl #.T W W W E ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` =.=.=.=.:.:.:.:.:.:.:.:.:.e.e.e.e.r.W H.NXSXGXGXGXGXGXGXGXDXzXg.r.f.f.f.r.=.=.g.`.fXBXAXGXGXGXGXGXGXGXGXGXAXjXH.t =.` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W T $.6 HXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHX~ ..W W W W E ~ ~ ~ ~ ~ ~ ~ ~ ` ` ` ` ` ` =.=.=.=.=.:.:.:.:.:.:.:.e.e.e.e.e.e.e.r.W |.CXGXGXGXGXGXGXGXGXBX1X,.f.f.f.f.h.h.f.,.~ d.3XVXAXGXGXGXGXGXGXGXGXGXDXsX' f ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W ..~ HXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHX$.R W W W W W E ~ ~ ~ ~ ~ ` ` ` ` ` ` =.=.=.=.=.=.:.:.:.:.:.:.:.e.e.e.e.e.r.r.r.,.w.>XFXGXGXGXGXGXGXGXSXNX`.=.f.h.h.h.h.f.f.f.f.=.~ ,XVXSXGXGXGXGXGXGXGXGXSXVXT.y ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W R $.HXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXX %.T W W W W W E ~ ~ ~ ~ ~ ` ` ` ` =.=.=.=.=.:.:.:.:.:.:.:.:.e.e.e.e.e.e.r.r.r.u.=.x.fXDXGXGXGXGXGXGXGXSXmXA.,.h.h.h.k.k.h.f.f.f.f.:.~ 5XFXGXGXGXGXGXGXGXGXGXCX:XW ~ ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W W T $.. HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHX8 $.T W W W W W W E ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.:.:.:.:.e.e.e.e.e.r.r.r.r.r.u.u.~ K.NXSXGXGXGXGXGXGXGXDXzXj.r.k.k.k.k.k.h.f.f.f.f.f.W V.VXSXGXGXGXGXGXGXGXGXDXuXw.f ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W T $.3 HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXY ..W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.:.e.e.e.e.e.e.r.r.r.r.u.u.u.u.~ |.CXGXGXGXGXGXGXGXGXBX2Xr.f.k.k.k.k.k.k.h.f.f.f.f.,.d.bXFXGXGXGXGXGXGXGXGXDXfXd.d =.` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W O.P HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXO.W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.e.e.e.e.r.r.r.r.r.r.u.u.u.u.r.w.>XFXGXGXGXGXGXGXGXSXNX'.,.k.k.k.k.k.k.k.h.h.f.f.f.e.y.kXFXGXGXGXGXGXGXGXGXDXfXg.d =.` ` ` ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W W W W W O.HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHX$.R W W W W W W W W E ~ ~ ~ ~ ~ ` ` ` ` =.=.=.:.:.:.:.e.e.r.r.r.r.u.u.u.u.u.u.f.=.b.fXDXGXGXGXGXGXGXGXSXmXJ.r.k.k.k.k.k.k.k.h.h.f.f.f.:.s.mXFXGXGXGXGXGXGXGXGXDXpXy.R =.` ` ` ~ ~ ~ ~ ~ E E W W W W W W W W W W W W W W W W W $.HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHX1.R W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.e.r.r.u.u.u.u.u.u.u.f.=.K.NXSXGXGXGXGXGXGXGXFXxXM.u.k.k.k.k.k.k.k.k.h.f.f.k.~ K.VXSXGXGXGXGXGXGXGXGXCX5X=.~ =.=.` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W W W W $.HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHX+ $.T W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.e.r.r.u.u.u.u.f.f.f.=.|.CXGXGXGXGXGXGXGXGXFXXFXGXGXGXGXGXGXGXGXFX9XA.b.u.r.r.u.u.h.h.h.u.r.O.w.:XCXSXGXGXGXGXGXGXGXGXSXhXL.a :.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W T $.* HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXV X.T W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.e.r.r.u.u.u.u.f.,.b.fXFXGXGXGXGXGXGXGXGXSXFXVXpX*X[.R.V.M.g.d.d.g.b.T.pXCXSXGXGXGXGXGXGXGXGXGXDXpXe.~ :.:.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W W T $.; HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHX| O.T W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.:.e.e.r.r.u.u.u.u.f.=.K.NXSXGXGXGXGXGXGXGXGXGXGXSXFXFXBXNXmXuX>X3X3XyXmXVXFXSXGXGXGXGXGXGXGXGXGXAXhXE.d :.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W T @.h HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXc @.T W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` ` =.:.:.:.:.:.e.e.e.r.r.u.u.u.u.=.|.BXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXSXSXFXFXFXFXFXSXSXGXGXGXGXGXGXGXGXGXGXAXNX>X~ =.e.:.:.:.=.` ` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W W W W W W W @.h HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXk @.T W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` =.=.:.:.:.:.e.e.e.r.r.r.u.u.r.w.>XFXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXZXNXeXe.~ e.:.:.:.:.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W @.h HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXc @.T W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.e.r.r.u.u.=.x.fXFXGXGXGXGXGXGXGXGXGXFXFXSXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXFXCXfXoX:.~ r.e.:.:.:.:.:.=.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W W @.h HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXc @.T W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.:.e.e.r.r.r.u.~ K.NXSXGXGXGXGXGXGXGXSXZX6XkXmXNXBXDXAXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGX0X'.S.~ =.u.e.e.e.:.:.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W W W @.h HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXk @.T W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.e.e.e.r.r.u.~ |.CXGXGXGXGXGXGXGXGXFX4X,.k.D.Q.,XkXmXNXDXSXSXGXGXGXGXGXGXGXGXGXGXGXXFXGXGXGXGXGXGXGXSXVX{.,.f.u.r.u.N.J.{.5XNXBXAXSXGXGXGXGXGXGXGXGXGXFXMXH.W r.u.r.e.e.e.:.:.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W W T @.h HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXo.O.T W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.e.r.O.s.fXFXGXGXGXGXGXGXGXSXmXJ.r.N.N.N.N.h.r.r.f.J.1XhXBXAXGXGXGXGXGXGXGXGXSXDXjX!.W e.u.r.e.e.e.:.:.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W W W W T @.g HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXB X.T W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` ` =.=.:.:.:.:.:.e.e.r.W H.NXSXGXGXGXGXGXGXGXDXuXM.u.k.k.N.N.N.N.N.h.,.e.D.>XNXSXGXGXGXGXGXGXGXGXSXZXjXE.W r.r.e.e.e.:.:.:.:.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W W T $.- HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXl @.T W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ` ` ` =.=.=.:.:.:.:.e.e.r.W |.CXGXGXGXGXGXGXGXGXBX2Xr.h.k.k.k.k.k.k.k.k.k.h.,.,.|.NXZXGXGXGXGXGXGXGXGXGXZXgXV.~ u.e.e.e.:.:.:.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W T $.% HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHX@ $.T W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.e.:.' >XFXGXGXGXGXGXGXGXSXNX{.,.k.k.k.k.k.k.k.k.k.k.k.k.u.~ `.NXSXGXGXGXGXGXGXGXGXSXCX>X=.e.r.r.e.e.:.:.:.:.:.=.=.` ` ` ~ ~ ~ ~ ~ ~ W W W W W W W W W T $.. HXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHX%.R W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ` ` ` ` =.=.:.:.:.:.e.~ s.fXFXGXGXGXGXGXGXGXSXNXJ.,.k.k.k.k.k.k.k.k.k.k.h.h.k.u.O.2XCXGXGXGXGXGXGXGXGXGXAXhXV.~ u.r.e.e.e.:.:.:.:.=.=.=.` ` ` ~ ~ ~ ~ ~ W W W W W W W W W W $.HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHX$.R W W W W W W W W W W W W W W W W W E ~ ~ ~ ~ ~ ~ ` ` ` ` ~ :.:.:.:.e.f Z.VXSXGXGXGXGXGXGXGXDXzXM.r.k.k.k.k.k.k.k.h.h.h.h.f.f.k.=.V.NXSXGXGXGXGXGXGXGXGXSXVX`.W r.e.e.e.e.:.:.:.:.=.=.=.` ` ` ~ ~ ~ ~ ~ ~ E W W W W W W W W $.HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXO.W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` ` =.~ Q a a W =.=.t XCXGXGXGXGXGXGXGXGXBX2Xr.f.k.k.k.k.k.k.h.h.h.h.f.f.f.f.r.y.kXFXGXGXGXGXGXGXGXGXGXBX,X~ :.e.e.e.:.:.:.:.:.:.=.=.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W W ~ ..HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXI O.W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ` a z.-X_.B.q.! u C.NXSXGXGXGXGXGXGXGXSXNX'.=.h.h.k.k.k.h.h.f.f.f.f.f.f.f.f.r.w.5XFXGXGXGXGXGXGXGXGXGXCX2X=.:.e.:.:.:.:.:.:.:.:.=.=.=.` ` ` ` ~ ~ ~ ~ ~ E W W W W W W O.P HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXk @.T W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ~ ~ t ).jXVXNXaX2X1XBXDXSXGXGXGXGXGXGXGXSXmXA.:.h.h.h.h.h.f.f.f.f.f.f.f.f.f.f.,.d.vXFXGXGXGXGXGXGXGXGXGXCX1X` =.:.:.:.:.:.:.=.=.=.=.=.=.` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W T $.; HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXo %.T W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ` y q.fXZXSXSXFXFXFXSXSXGXGXGXGXGXGXGXGXFXxXj.r.f.h.h.h.f.f.f.f.f.f.f.f.u.u.f.W B.NXSXGXGXGXGXGXGXGXGXSXBXoXW :.:.:.:.:.:.=.=.=.=.=.` ` ` ` ` ` ~ ~ ~ ~ ~ ~ W W W W W W %. HXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHX$.R W W W W W W W W W W W W W W W W W W W E ~ ~ ~ ` e !.CXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXFX+Xd ,.f.h.h.h.f.f.f.f.f.f.u.u.u.f.,.T :XFXGXGXGXGXGXGXGXGXGXSXNXE.f :.:.:.:.:.=.=.=.=.` ` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ W W W W R $.HXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHX~ ..W W W W W W W W W W W W W W W W W W W W E ~ ~ a _ aXFXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXFX7XV.s.:.=.:.,.u.f.f.f.f.u.u.u.r.~ s ~.VXSXGXGXGXGXGXGXGXGXGXAXhXV.d :.:.=.=.=.=.=.` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W W W W O.E HXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXg $.T W W W W W W W W W W W W W W W W W W W E ~ ~ e G.hXAXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXFXVXpX*X_.Z.x.t.:.` ~ ~ ~ ~ ~ ' x.*XVXSXGXGXGXGXGXGXGXGXGXGXDXuXw.W :.=.=.=.=.` ` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W W W W W W T $.; HXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHX %.R W W W W W W W W W W W W W W W W W W W W ~ d T qXgXBXFXSXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXFXFXBXNXaX>X,X[._.T.T.E.|.:XNXCXSXGXGXGXGXGXGXGXGXGXGXSXVX Xd =.=.=.=.` ` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ E W W W W W W R %.HXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHX@.W W W W W W W W W W W W W W W W W W W W W ~ R ` s.H.oXkXNXNXCXFXSXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXSXDXFXCXCXBXVXVXBXCXFXSXSXGXGXGXGXGXGXGXGXGXGXGXAXhXm.a :.` =.` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W W W W W W W W W W @.HXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXx @.T W W W W W W W W W W W W W W W W W W W W ~ ~ y t a _ g.L.oXkXhXVXCXFXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXGXSXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXSXBX:Xf ~ ` ` ` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ E W W W W W W W W W T $.h HXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHX%.R W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ d a t a ' s.R.oXnXDXSXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXGXZXhXg.y =.` ` ` ` ` ~ ~ ~ ~ ~ ~ ~ ~ ~ E ~ E W W W W W W W W W W R %.HXHXHXHXHXHXHXHXHXHXHX", -"HXHXHXHXHXHXHXHXHXHXHXO.~ W W W W W W W W W W W W W W W W W W W W W ~ ~ ~ ~ ~ ` ` ~ W a a d ! .= 5 5 5 w w w w w p w p p p p p p p k k 0 OXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXR.J ~ ' ' ' ' ' ] ] { { { { { ........O.O. .W.xXxXxXxXxXxXxXxXxXxXW.t.i.i.i.g.g.g.k.k.x.k.l.l.B.z.vXvX", +"vX- - - % % % % % % % % % % % o Z xXxXxXxXxXxXxXxXxXxX$X= 6 6 5 w w w w w w w p w p p p p k k k k g =XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxX@XU Y ~ Y ' ' ' ' ] ] ] ] { { { ............ .5XxXxXxXxXxXxXxXxXxXxXv.i.i.i.g.i.g.g.g.k.k.k.k.l.B.l.vXvX", +"vX- - % % % % % % % % % % % % % 9 jXxXxXxXxXxXxXxXxXxXjXw 2 6 6 6 w w w w w p w p p p p p p p z z t k -XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxX@XU T ~ ~ ' ~ ' ' ' ' ' ] ] ] { { { ..{ .... .*.zXxXxXxXxXxXxXxXxXxXaXy.t.y.i.i.i.g.g.g.g.k.k.k.k.C.x.vXvX", +"vXvX: % % % % % % % % % % % % % XXxXxXxXxXxXxXxXxXxXxX6.# 4 5 6 6 6 w w w w w w p w p p p p k k z q g OXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXR.J Y Y Y ~ ~ ~ Y ' ' ' ' ' ] ] ] { { { { ....[ Q.xXxXxXxXxXxXxXxXxXxX.xXxXxXxXxXxXxXxXxXxXqX2 7 4 6 6 5 5 w w w w w w w p w p p p p k k t 0 9.uXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXtX8.S Y Y Y Y / ' ~ ~ ' ' ' ' ' ' ] ] ] { { { ..[ ..sXxXxXxXxXxXxXxXxXxXxXP.+.y.u.i.i.i.g.g.g.g.g.k.k.k.x.vXvXvX", +"vXvX% - % % % % % % % % % % % - O 8 cXxXxXxXxXxXxXxXxXxXxX:.# 4 6 6 6 5 w w w w w p w w p p p p p p a k k 0 c r.iXxXxXxXxXxXxXxXxXxXxXxXxXxXtXe.G S Y Y Y Y Y Y Y Y ~ Y ' Y ' ' ' ] ] ] ] ] { { } L.xXxXxXxXxXxXxXxXxXxXsX$.+.t.y.y.y.u.y.g.y.g.g.g.g.z.k.vXvXvX", +"vXvX% : % % % % % % % % % % % % % XXxXxXxXxXxXxXxXxXxXxXwX@ 2 6 3 6 6 6 w w 5 w w w w p p p p p p a k a k t 0 K 9.OXiXcXxXxXxXxXxXjXtXOX8.D S G H H H Y H Y Y Y Y ' Y ' ' ~ ' ' ' ' ' ] ] { ] } dXxXxXxXxXxXxXxXxXxXxX:Xo.+.u.u.u.y.y.i.i.i.p.i.i.g.B.x.vXvXvX", +"vXvXvX> % % % % % % % % % % % % % o C xXxXxXxXxXxXxXxXxXxXxX;.+ 3 6 6 6 6 5 6 w w w w w w u p w p p p p p z k z k 0 0 l E <.1.w.9.<.E D h h D L P P H H H Y Y Y Y Y Y Y Y ' ~ ~ ' ' ' ' ] ] ] ` T.xXxXxXxXxXxXxXxXxXxXxXa.o.@.#.u.y.y.y.y.i.y.i.i.i.g.z.vXvXvXvX", +"vXvXvX% % % % % % % % % % % % % % % O rXxXxXxXxXxXxXxXxXxXxXrX: 3 3 3 4 4 6 6 5 5 w w w w p w p p p p p p k p k k k l l g d d d d h v D K K K L P G P P H H Y R Y Y ~ ' Y ' ~ ' ' ' ' ' ' ] ] | hXxXxXxXxXxXxXxXxXxXxX5Xo.+.#.@.@.u.t.u.y.y.y.y.i.i.k.i.vXvXvXvX", +"vXvXvX% > % % % % % % % % % % % % % 4.xXxXxXxXxXxXxXxXxXxXxX7.+ 3 6 4 4 4 5 5 6 w 5 w w w u w u u p p p a k p k z k l l l l l v l l K K K L K L K P P P H H Y Y Y Y Y Y Y ~ Y Y ' ~ ' ' ' T +XxXxXxXxXxXxXxXxXxXxXxXL. .@.O.#.y.@.@.y.y.y.y.y.i.i.x.i.vXvXvXvX", +"vXvXvXvX: - % % % % % % % % % % % % $ % fXxXxXxXxXxXxXxXxXxXxXcXn @ 3 3 7 7 6 6 6 w 6 w w w w w u p u p p p a a p k k k l k k l l l K l K K K K K L L P P P H Y H H Y Y Y Y Y ' ~ ~ ' ~ ' ! 8.zXxXxXxXxXxXxXxXxXxXxXdX..O.O.+.#.O.@.y.@.u.y.y.y.y.i.x.vXvXvXvXvX", +"vXvXvXvX% - % % % % % % % % % % % % % 4.xXxXxXxXxXxXxXxXxXxXxXqX& ; 3 3 7 4 4 6 6 6 w w w w w u u u p p p p p k p k k k k l l l l l K x x K K K K K P L L P H H R Y Y Y ' Y Y ~ ~ ~ ' T ' aXxXxXxXxXxXxXxXxXxXxXxXL.[ O.O.O.O.@.@.O.y.@.y.y.y.y.g.y.vXvXvXvXvX", +"vXvXvXvX% > % % % % % % % % % % % % % $ % rXxXxXxXxXxXxXxXxXxXxXxX{.+ 3 3 3 7 4 6 6 6 6 6 w w w u u w p u p p p w k p k k l k k l l l l l x x K K K L K K H P P H H R Y Y Y Y ~ ~ ~ ~ T T *XxXxXxXxXxXxXxXxXxXxXxXtX[ ..O.O.O.o.O.@.@.@.@.y.@.y.y.x.y.vXvXvXvXvX", +"vXvXvXvXvX: % % % % % % % % % % % % % % >.xXxXxXxXxXxXxXxXxXxXxXxX3.+ 3 3 3 3 3 6 6 6 w w w w w w u w u u p p z p p k k p k l z l l l x l K l K K K K G K L P R H R R Y Y Y Y Y ~ Y J R.xXxXxXxXxXxXxXxXxXxXxXxXb.[ ....O.O.O.@.O.@.+.@.y.y.@.y.h.vXvXvXvXvXvX", +"vXvXvXvXvX% - % % % % % % % % % % % % % % o wXxXxXxXxXxXxXxXxXxXxXxXzXZ + : 3 3 6 3 4 6 5 5 5 5 w w w u u u p u p p p k p k k k k k l l l l l l K K K K K K K P P P P P Y Y Y Y Y Y U 0.xXxXxXxXxXxXxXxXxXxXxXxX*X} ..........O.O.O.O.@.@.@.@.#.x.$.vXvXvXvXvXvX", +"vXvXvXvXvXvX> % % % % % % % % % % % % % $ o B cXxXxXxXxXxXxXxXxXxXxXxXjXV + 3 3 3 3 3 6 6 6 6 5 5 w w u w u u u u u p p p p k k z k k l l l l K l K l K K K K P P P P P H H H Y Y J q.zXxXxXxXxXxXxXxXxXxXxXxXhX&.} { { ........O.O.O.@.@.@.@.@.g.vXvXvXvXvXvXvX", +"vXvXvXvXvXvX% : % % % % % % % % % % % % % % 6.xXxXxXxXxXxXxXxXxXxXxXxXjXV + : 3 3 7 6 4 4 6 5 5 5 w w w u u u p u p p p k p k k k k l l l l l l l K K K K K G L L P P P H R R S 8.zXxXxXxXxXxXxXxXxXxXxXxXxXT.T { { { { { ....O.O.O.O.@.@.@.h.@.vXvXvXvXvXvXvX", +"vXvXvXvXvXvXvX> % % % % % % % % % % % % % % $ o &XxXxXxXxXxXxXxXxXxXxXxXxXcXZ + 3 3 3 3 4 6 6 6 6 5 5 5 w w u u u p u p p p k p k k k k k l l l l K l l K K K K K L L P P H P S 0.zXxXxXxXxXxXxXxXxXxXxXxXxX*X! ] { ] { { { ......O.O.O.O.O.@.h.vXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvX% - % % % % % % % % % % % % % % o 8 gXxXxXxXxXxXxXxXxXxXxXxXxXzX3.+ ; 3 3 4 4 4 6 6 6 5 5 5 w w u u u u u p p p p p k k k k k k l l l l K l K K K L K L L P G S d.xXxXxXxXxXxXxXxXxXxXxXxXxXsX| ! ' ] ] { { { { { { O.O...O.O.$.O.vXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvX> % % % % % % % % % % % % % % % C xXxXxXxXxXxXxXxXxXxXxXxXxXxX{.; + 3 3 3 4 4 6 6 5 5 5 w w w u u u p p u p p p p p k k k k l l l l l K l l K K K K K A P OXxXxXxXxXxXxXxXxXxXxXxXxXxXzX-.T ' ' ] ] ] { { { { ..{ ....O.@.h.vXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvX% > % % % % % % % % % % % % % % % 4.xXxXxXxXxXxXxXxXxXxXxXxXxXxXqXV + ; 3 4 4 4 6 6 5 5 5 5 w w w u u p p p p p k p k k k k k l l l l l l K K K K D h <.iXxXxXxXxXxXxXxXxXxXxXxXxXxXxXf.U ^ ~ ' ' ] ] ] ] { { { { { O.{ u.O.vXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvX- % % % % % % % % % % % % % % % $ {.xXxXxXxXxXxXxXxXxXxXxXxXxXxXzX|.3 + 3 4 4 4 6 6 6 5 5 w w w u u u u u p p p p k k k k k k l l l l l l K D f K oXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXR.U ~ ~ ' ' ' ' ] ] ] { { { { ..{ O.@.vXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvX% > % % % % % % % % % % % % % % % % o XXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXrX>.* @ 2 3 4 6 6 5 5 5 5 w w u u u u u p p p p p k k k k k l l l v l d v w.iXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxX#XU Y ~ ~ ' ' ' ' ' ' ' ] { { { { { u.{ vXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvX% - % % % % % % % % % % % % % % - O o XXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXqX;.2 + ; 4 4 6 6 5 5 5 w w u u u u u p p p p p p k k k k k g r l w.yXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxX#XU U Y ~ ~ ~ ~ ~ ' ' ' ' ' { ] { { O.{ vXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvX> % % % % % % % % % % % % % % % % O o XXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXrX7.b @ # , 4 5 5 5 5 5 5 u u u u u p p p k p k g y r t E r.iXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXoXJ Y Y Y ~ ~ ~ ~ ' ~ ' ' ' ' ] { ] { $.vXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvX% > % % % % % % % % % % % % % % % - O . |.xXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXcX&X5.n 2 # = , 1 5 5 w 5 u u u u y q q 0 0 p W r.=XzXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXR.S Y Y Y Y Y Y ~ ~ ~ ~ ' ' ' ' ' ' ' $.' vXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvX- - % % % % % % % % % % % % % % % % % 4.zXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXzXeXXX5.:.M b 5 5 < , q q u m N 2.r.$XiXzXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXzX0.S H H H Y Y Y ~ ~ ~ Y ' ~ ~ ' ' ' ' ..{ vXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvX: - % % % % % % % % % % % % % % % % % Z fXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXcXgXuXeXqX=XqXeXuXjXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXdX_ h L H H Y Y Y Y Y Y ~ ~ Y ' ~ ~ ^ ' ] { vXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvX% > % % % % % % % % % % % % % % % % % % 9 ;XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxX%XI A L P G P H H Y Y Y Y Y ~ Y ~ ~ ' ~ ' X.vXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvX% > % % % % % % % % % % % % % % % % % % O o 4.cXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXjX0.h D L K P L P H H H Y Y Y Y Y ~ ~ Y ~ ~ X.^ vXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- > % % % % % % % % % % % % % % % % % % O . 8 XXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxX%XE f D K K K L L P P P H Y Y H Y Y Y ~ / ~ { ^ vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- - % % % % % % % % % % % % % % % % % % - O o C eXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXeX<.d h K v x K K K K L L P P H H H Y Y Y Y Q ] ~ vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- > % % % % % % % % % % % % % % % % % % % % o O 4.rXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXeX9.g d l l x x K K K K K L L L P P P Y H H Y Y ^ / vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- - % % % % % % % % % % % % % % % % % % % % O % C wXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxX=X1.g r k k l l l x x x x K K K L L L P P P H H Y ^ ~ vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- - % % % % % % % % % % % % % % % % % % % % % % o V XXfXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXiX XW y r g z k k l l l x x x x K K K K K G L P P H H / Q vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- > % % % % % % % % % % % % % % % % % % % % % % % o % C XXrXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXuX X2.a 0 0 p a p k k k l k k x l x l x K K K K K L L P H / Y vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- 9 % % % % % % % % % % % % % % % % % % % % % % % % O % V 4.XXrXjXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXjXyXXX5.M u 1 q u p p p k p k k k k l k l l v x x K x K K K K L L / Y vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX% - % % % % % % % % % % % % % % % % % % % % % % % % % % % o O 8 V C 6.[. XXXXX&X$XXX X[.5.;.M b < 1 1 < w u u u u p p p p k k k k k l k l l l v l K K K K L L / P vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- > - - % % % % % % % % % % % % % % % % % % % % % % % % % % % $ $ $ + . . + @ @ @ # @ + = * * , 2 w w w w w w w w u p p p p a k k a k l k l l l l x x x x K L ( P vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- - % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % ; ; % : : 3 3 3 7 2 3 6 6 w 7 7 7 w w w w w u p u p p p p k p k k k k l l l x x x x L I vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- > % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % : : : : 3 : 3 3 3 7 7 6 6 w w 6 w w w w w u u u p p p p a k k k k k k k l k x x ( K vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- > - % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % : % : : : 3 : 3 3 3 3 7 3 6 6 w w w w w u u u u u p p p p p p k k k l k x x I x vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- > % % % % % % % % % % % % % % % % % % % % % % % % % % % % % : % : - : 3 3 3 3 3 3 6 4 6 6 7 w w w w w w u u u p p p p p p k k a k l x x vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- : > % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % : : : : : : 3 3 3 4 4 4 w w w 7 w w w w u u u u p p p k a p k c l z vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- > - % % % % % % % % % % % % % % % % % % % % % % % % % % % % - - ; : : 3 : 3 3 3 4 4 4 6 6 6 w w w w u u u u u p p p p l c k vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX% > : % % % % % % % % % % % % % % % % % % % % % % % % % % - % : ; : 3 ; : 3 3 4 7 3 4 6 6 6 5 5 5 w u u u u p p a z a vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- > > % % % % % % % % % % % % % % % % % % % % % % % % % % : : : : : : 3 3 3 3 3 7 6 6 6 5 5 5 w w u u s a a a vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- - > - % % % % % % % % % % % % % % % % % % % % % % % % % % : : : 3 3 3 3 3 7 3 7 6 6 5 5 5 5 i s u s vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX% - : > - % % % % % % % % % % % % % % % % % % % % % % ; ; % : : : 3 3 3 3 3 3 6 6 6 e i i i u vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- % > > - % % % % % % % % % % % % % % % % % % % % % : : : : 3 % 3 3 3 4 w e e 5 e vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- % % > : 9 % % % % % % % % % % % - % % % % - - - : : : 3 6 9 5 7 4 e vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX- % % % - : > > : - : : % % % : - - : > > : : : : : 2 vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX% % % % % - % % - % % - vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX" }; diff --git a/share/pixmaps/bitcoin16.png b/share/pixmaps/bitcoin16.png index 55374790..83e9aab7 100644 Binary files a/share/pixmaps/bitcoin16.png and b/share/pixmaps/bitcoin16.png differ diff --git a/share/pixmaps/bitcoin16.xpm b/share/pixmaps/bitcoin16.xpm index 40a0624a..57f21cc4 100644 --- a/share/pixmaps/bitcoin16.xpm +++ b/share/pixmaps/bitcoin16.xpm @@ -1,181 +1,174 @@ /* XPM */ -static char *bitcoin__[] = { +static char *bitcoin16[] = { /* columns rows colors chars-per-pixel */ -"16 16 159 2", -" c #CA7C1E", -". c #CB7D1E", -"X c #D1811E", -"o c #D0801F", -"O c #D1801F", -"+ c #D3821F", -"@ c #D7831F", -"# c #EE8D18", -"$ c #F4931F", -"% c #D78625", -"& c #D88520", -"* c #D98521", -"= c #D98620", -"- c #D78B2D", -"; c #DF8D2A", -": c #DF8F2F", -"> c #DF943B", -", c #D8913C", -"< c #D8923E", -"1 c #DF953E", -"2 c #E28B23", -"3 c #E38B23", -"4 c #EA9023", -"5 c #EB9023", -"6 c #ED9122", -"7 c #ED9123", -"8 c #EE9123", -"9 c #EE9223", -"0 c #F39421", -"q c #F19423", -"w c #F39523", -"e c #F79521", -"r c #F59422", -"t c #F49623", -"y c #F69622", -"u c #F79623", -"i c #F09324", -"p c #F19424", -"a c #F19525", -"s c #F49624", -"d c #F59625", -"f c #F49725", -"g c #F79624", -"h c #F79724", -"j c #F69725", -"k c #F79725", -"l c #F69726", -"z c #F79726", -"x c #F89621", -"c c #F89722", -"v c #F89723", -"b c #F89724", -"n c #F89824", -"m c #F89825", -"M c #F99825", -"N c #F89925", -"B c #F89926", -"V c #F89927", -"C c #F99927", -"Z c #F0972E", -"A c #F7992A", -"S c #F79A2B", -"D c #F79B2C", -"F c #F69A2D", -"G c #F79D2F", -"H c #F89929", -"J c #F89A28", -"K c #F89A29", -"L c #F99A29", -"P c #F99B29", -"I c #F89A2A", -"U c #F89A2B", -"Y c #F99B2B", -"T c #F89B2C", -"R c #F89C2C", -"E c #F99C2D", -"W c #F99C2E", -"Q c #F89D2E", -"! c #F99D2F", -"~ c #E29335", -"^ c #E49639", -"/ c #E2983F", -"( c #F79F35", -") c #F99E31", -"_ c #F89E32", -"` c #F99E32", -"' c #F9A033", -"] c #F9A035", -"[ c #F9A135", -"{ c #F9A036", -"} c #F9A136", -"| c #F9A137", -" . c #F3A03F", -".. c #F7A43F", -"X. c #F8A139", -"o. c #F9A23A", -"O. c #FAA33B", -"+. c #FAA43E", -"@. c #FAA43F", -"#. c #EF9F41", -"$. c #EEA244", -"%. c #ECA34B", -"&. c #F8A440", -"*. c #F9A541", -"=. c #F9A644", -"-. c #F9A947", -";. c #F0A349", -":. c #F5A648", -">. c #F1A74E", -",. c #F7AA4F", -"<. c #E4A458", -"1. c #E4A55B", -"2. c #E8A95E", -"3. c #F2A950", -"4. c #F4AA52", -"5. c #FBAF55", -"6. c #E4A860", -"7. c #EAAC63", -"8. c #EBAF68", -"9. c #F2AF61", -"0. c #EBB16C", -"q. c #F6B568", -"w. c #E3AF71", -"e. c #EBBE89", -"r. c #E0BC93", -"t. c #E3C199", -"y. c #E6C59D", -"u. c #EAC89E", -"i. c #E7C8A2", -"p. c #EACBA6", -"a. c #EBCFAF", -"s. c #F1CCA0", -"d. c #E7CEB1", -"f. c #ECD1B0", -"g. c #E5D2BB", -"h. c #E8D2B8", -"j. c #DFDFDF", -"k. c #E7D5C1", -"l. c #E7D7C4", -"z. c #E5D7C7", -"x. c #E7DACB", -"c. c #EADAC8", -"v. c #E9DCCC", -"b. c #EDDFCE", -"n. c #E5DDD3", -"m. c #E4DFD9", -"M. c #ECE0D1", -"N. c #E4E1DD", -"B. c #EDE3D8", -"V. c #EAE4DD", -"C. c #ECE5DC", -"Z. c #E2E2E2", -"A. c #E5E2E0", -"S. c #E4E4E4", -"D. c #E7E7E7", -"F. c #EAEAE9", -"G. c gray92", -"H. c #EEEEEE", -"J. c None", +"16 16 152 2 ", +" c #0F3C68", +". c #0E3F69", +"X c #0D3D6C", +"o c #0D3E6D", +"O c #113B67", +"+ c #103E6A", +"@ c #00476B", +"# c #0F456E", +"$ c #00486B", +"% c #18426C", +"& c #19446C", +"* c #18446F", +"= c #19446F", +"- c #0E4E71", +"; c #174572", +": c #1A4670", +"> c #184572", +", c #005674", +"< c #005876", +"1 c #15597B", +"2 c #00647D", +"3 c #00677F", +"4 c #0B637F", +"5 c #13607D", +"6 c #006F83", +"7 c #126786", +"8 c #007486", +"9 c #05708B", +"0 c #007B8B", +"q c #11718C", +"w c #037B92", +"e c #3C6084", +"r c #3F6587", +"t c #27768E", +"y c #2A738C", +"u c #39718E", +"i c #3D7993", +"p c #3C7A94", +"a c #4C7391", +"s c #407993", +"d c #517D97", +"f c #00818E", +"g c #0E8695", +"h c #018A9B", +"j c #128094", +"k c #04919A", +"l c #00969B", +"z c #04969D", +"x c #009D9F", +"c c #398E9F", +"v c #3E8C9F", +"b c #0095A1", +"n c #0C96A0", +"m c #00A4A3", +"M c #09A2A7", +"N c #00A9A6", +"B c #00A5AB", +"V c #08A9A9", +"C c #00B1AB", +"Z c #00B5AE", +"A c #00B0B1", +"S c #00BDB0", +"D c #07BAB6", +"F c #5B839D", +"G c #67839E", +"H c #69849F", +"J c #4FB7BB", +"K c #55B4BA", +"L c #5EB0B9", +"P c #63A2B1", +"I c #63A3B1", +"U c #60A6B3", +"Y c #64AEB8", +"T c #61B4BC", +"R c #00C0B2", +"E c #00C2BD", +"W c #01CDC0", +"Q c #00CDC4", +"! c #04CFC5", +"~ c #1ECAC1", +"^ c #1ECEC3", +"/ c #00D9C5", +"( c #02D9CA", +") c #02DEC9", +"_ c #00DDCC", +"` c #34C4C0", +"' c #34C8C2", +"] c #24D7C9", +"[ c #22D9CB", +"{ c #2BDBCD", +"} c #29DBCE", +"| c #2FD9CC", +" . c #03E1CB", +".. c #00E6CD", +"X. c #00E7CF", +"o. c #00EBCF", +"O. c #00EDD1", +"+. c #00ECD2", +"@. c #00EED2", +"#. c #00EED4", +"$. c #00F0D4", +"%. c #00F2D8", +"&. c #00F4D9", +"*. c #00F4DA", +"=. c #29E1D2", +"-. c #3EE6D6", +";. c #35EFDB", +":. c #37EEDA", +">. c #3BEBD9", +",. c #25F1DB", +"<. c #26F3DC", +"1. c #02FCE1", +"2. c #00FDE2", +"3. c #00FFE3", +"4. c #02FFE3", +"5. c #00FFE5", +"6. c #5AC0C2", +"7. c #5FC5C6", +"8. c #5BC8C7", +"9. c #60CFCC", +"0. c #95A8BB", +"q. c #9BB5C4", +"w. c #9FB8C6", +"e. c #8DCBCF", +"r. c #9AC8CF", +"t. c #9AC9D0", +"y. c #9ACCD2", +"u. c #86DFDB", +"i. c #87DFDB", +"p. c #91D1D4", +"a. c #93DBDA", +"s. c #99DFDE", +"d. c #9CDCDC", +"f. c #ABD1D8", +"g. c #9DE0DF", +"h. c #A2E9E5", +"j. c #ABF7EF", +"k. c #B1F8F1", +"l. c #DFE3E9", +"z. c #DDE8EC", +"x. c #C0FAF3", +"c. c #DFF8F6", +"v. c #E8EAEF", +"b. c #E8EFF2", +"n. c #E3F9F7", +"m. c #FAFBFC", +"M. c #FBFDFD", +"N. c #F8FEFE", +"B. c #F9FEFE", +"V. c #FEFDFE", +"C. c #FEFEFE", +"Z. c None", /* pixels */ -"J.J.J.J.J.J.J.1 > J.J.J.J.J.J.J.", -"J.J.J.J.J./ ..| ' ( ~ J.J.J.J.J.", -"J.J.J.< *.{ V $ r U W _ - J.J.J.", -"J.J., o.J 0 # <.w.$.F N H % J.J.", -"J.J.o.T e 1.r.k.x.t.S z B u J.J.", -"J.^ [ Y ! #.z.H.M.n.0.d n m 2 J.", -"J.X.) | =. .h.B.5.f.j.;.v B d J.", -": Q M ` &.>.A.V.p.c.l.4.E n d = ", -"; I b A Z 2.D.s.u.F.a.-.} C w & ", -"J.l g y 6.m.G.q.3.b.Z.,.] D 8 J.", -"J.3 k c %.d.C.v.N.S.y.@.L a * J.", -"J.J.j z x 8.i.g.e.9.+.W t 6 J.J.", -"J.J.+ s h G :.7.O.R B s 7 . J.J.", -"J.J.J.O i f P L K d p 5 J.J.J.", -"J.J.J.J.J.@ 9 q i 4 + J.J.J.J.J.", -"J.J.J.J.J.J.J.X o J.J.J.J.J.J.J." +"Z.Z.Z.Z.Z._ X.#.$.%.Z.Z.Z.Z.Z.Z.", +"Z.Z.Z.D Q ( =.>.<.1.5.*.Z.Z.Z.Z.", +"Z.Z.M A 9.c.C.M.M.;.o.1.*.Z.Z.Z.", +"Z.g b p.C.a.' ^ } .k.x.5.+.Z.Z.", +"Z.w Y C.J x N Z S [ M.j.o.5.Z.Z.", +"5 q b.y.f z 7.s.u.h.] ) :.1.+.Z.", +"- p C.c 8 T C.C.C.u.S | C.,.+.Z.", +". d M.t 3 f.C.C.C.g.C ~ C.-.o.Z.", +"O r C.i < P C.C.C.6.m ` C.} / Z.", +"% ; v.w.$ 4 P r.L k l d.n.! W Z.", +"Z.o H C.F @ , 2 8 0 K C.8.E Z.Z.", +"Z.= o 0.C.q.s y v r.C.e.B V Z.Z.", +"Z.Z.= o G l.C.m.C.z.U h n Z.Z.Z.", +"Z.Z.Z.= o > e a u 7 9 j Z.Z.Z.Z.", +"Z.Z.Z.Z.Z.= + . # 1 Z.Z.Z.Z.Z.Z.", +"Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z." }; diff --git a/share/pixmaps/bitcoin256.png b/share/pixmaps/bitcoin256.png index 1d42116e..6bda6523 100644 Binary files a/share/pixmaps/bitcoin256.png and b/share/pixmaps/bitcoin256.png differ diff --git a/share/pixmaps/bitcoin256.xpm b/share/pixmaps/bitcoin256.xpm index 87bb35cd..0773509b 100644 --- a/share/pixmaps/bitcoin256.xpm +++ b/share/pixmaps/bitcoin256.xpm @@ -1,465 +1,501 @@ /* XPM */ -static char *bitcoin___[] = { +static char *bitcoin256[] = { /* columns rows colors chars-per-pixel */ -"256 256 203 2", -" c #BE741B", -". c #C1761B", -"X c #C6791C", -"o c #CC7C1D", -"O c #D07F1D", -"+ c #C67B21", -"@ c #CC7E21", -"# c #D4821E", -"$ c #D9841F", -"% c #ED8E1D", -"& c #EF911F", -"* c #CF8022", -"= c #D48323", -"- c #DB8621", -"; c #DD8922", -": c #D58729", -"> c #D6882B", -", c #DE8C2A", -"< c #CE8C3C", -"1 c #D28934", -"2 c #D98E32", -"3 c #D28E3C", -"4 c #DF9132", -"5 c #D6903E", -"6 c #DD933B", -"7 c #E58C22", -"8 c #E98F23", -"9 c #E38F2B", -"0 c #E88F28", -"q c #ED9124", -"w c #E6922D", -"e c #EB942B", -"r c #EF982F", -"t c #F59624", -"y c #F89723", -"u c #F79826", -"i c #F89825", -"p c #F1972A", -"a c #F59A2C", -"s c #F89B2B", -"d c #E59534", -"f c #EA9632", -"g c #EE9933", -"h c #E3963B", -"j c #E6993D", -"k c #EC9C3B", -"l c #F49C33", -"z c #F99E32", -"x c #F29E3A", -"c c #F7A037", -"v c #F9A036", -"b c #F5A13C", -"n c #F9A33B", -"m c #CE9147", -"M c #D29245", -"N c #DC9641", -"B c #DD9846", -"V c #D2954B", -"C c #DC9A4B", -"Z c #E59C44", -"A c #EA9E43", -"S c #E39E4B", -"D c #E89F49", -"F c #F09F40", -"G c #EDA145", -"H c #E6A14D", -"J c #EBA34B", -"K c #F4A443", -"L c #F9A642", -"P c #F7A847", -"I c #FAA846", -"U c #F3A64A", -"Y c #F8A748", -"T c #F5A94D", -"R c #FAAA4B", -"E c #E6A454", -"W c #EBA552", -"Q c #EDA856", -"! c #E4A55B", -"~ c #E8A75B", -"^ c #E7A95E", -"/ c #EBA95B", -"( c #F0A751", -") c #F4AB53", -"_ c #FAAE53", -"` c #F4AE5A", -"' c #F8AF59", -"] c #FAB057", -"[ c #F6B15E", -"{ c #FAB25B", -"} c #DFAD6F", -"| c #DCAE77", -" . c #DFB27D", -".. c #E5AA64", -"X. c #E8AB61", -"o. c #E5AE6C", -"O. c #E6B06F", -"+. c #ECB16C", -"@. c #F5B365", -"#. c #FBB562", -"$. c #FBB867", -"%. c #F5B66B", -"&. c #FAB768", -"*. c #F4B86F", -"=. c #FBB96A", -"-. c #E1AE71", -";. c #E5B174", -":. c #EBB573", -">. c #EFB977", -",. c #E5B47A", -"<. c #EEBA7B", -"1. c #F3B770", -"2. c #F3B974", -"3. c #FBBC72", -"4. c #F3BC7B", -"5. c #F8BF7A", -"6. c #FAC079", -"7. c #DCB382", -"8. c #DFBB8F", -"9. c #DABB96", -"0. c #DBBD99", -"q. c #E2B682", -"w. c #E4B985", -"e. c #ECBD84", -"r. c #E3BB8B", -"t. c #EABF8C", -"y. c #F1BE83", -"u. c #E2BE92", -"i. c #D3BDA2", -"p. c #DEC09C", -"a. c #EEC28D", -"s. c #F4C286", -"d. c #F8C282", -"f. c #F3C48B", -"g. c #E7C297", -"h. c #ECC393", -"j. c #E2C29D", -"k. c #EAC69B", -"l. c #ECC89F", -"z. c #F1C694", -"x. c #F2C897", -"c. c #F1CA9B", -"v. c #DBC2A3", -"b. c #D6C2AB", -"n. c #DDC7AD", -"m. c #DEC9AF", -"M. c #D3C4B3", -"N. c #DDCAB3", -"B. c #D2C7B9", -"V. c #D6C9BA", -"C. c #DDCEBB", -"Z. c #DFD0BE", -"A. c #E2C5A2", -"S. c #E8C7A0", -"D. c #E6C9A5", -"F. c #EBCBA4", -"G. c #E2C7A8", -"H. c #E3CAAC", -"J. c #EBCDA9", -"K. c #EFD2AF", -"L. c #F3D1A7", -"P. c #F1D1A9", -"I. c #E4CEB3", -"U. c #E8CFB1", -"Y. c #E1CFBA", -"T. c #E6D0B6", -"R. c #E9D1B4", -"E. c #E4D2BC", -"W. c #EAD4BA", -"Q. c #F4D5B0", -"!. c #F4D9B9", -"~. c #CDCDCD", -"^. c #D5CCC3", -"/. c #D4CFCA", -"(. c #DED2C3", -"). c #D3D1CE", -"_. c #DED6CC", -"`. c #D5D5D5", -"'. c #DBD7D1", -"]. c #DEDAD4", -"[. c #DDDDDC", -"{. c #E3D5C3", -"}. c #E9D7C1", -"|. c #EBD9C4", -" X c #E1D6CA", -".X c #E3D9CD", -"XX c #EADDCD", -"oX c #E1DBD4", -"OX c #E8DFD4", -"+X c #E1DEDB", -"@X c #EDE3D7", -"#X c #E3E1DE", -"$X c #E8E3DC", -"%X c #F6E5D2", -"&X c #F4EBDF", -"*X c #E4E4E4", -"=X c #ECE7E2", -"-X c #EDE9E4", -";X c #ECECEC", -":X c #F0EBE7", -">X c #F4F4F4", -",X c #FEFEFE", -" c #0C5274", +", c #0C5C7B", +"< c #065877", +"1 c #145375", +"2 c #195276", +"3 c #185678", +"4 c #175D7C", +"5 c #18597A", +"6 c #145678", +"7 c #26577B", +"8 c #295177", +"9 c #0B637E", +"0 c #04627D", +"q c #16617F", +"w c #1A5E81", +"e c #355E81", +"r c #046C83", +"t c #0B6C85", +"y c #096681", +"u c #166581", +"i c #196383", +"p c #156B85", +"a c #186C89", +"s c #047186", +"d c #027689", +"f c #0B758A", +"g c #037C8D", +"h c #0C7B8E", +"j c #0A7187", +"k c #14738A", +"l c #127A8E", +"z c #177389", +"x c #0C7F90", +"c c #127E90", +"v c #197991", +"b c #286684", +"n c #396787", +"m c #287B91", +"M c #357891", +"N c #46698A", +"B c #497994", +"V c #557895", +"C c #43728F", +"Z c #607E9A", +"A c #028492", +"S c #0C8493", +"D c #018A96", +"F c #038E98", +"G c #0F8E9A", +"H c #0C8996", +"J c #118394", +"K c #108997", +"L c #108B98", +"P c #178999", +"I c #01949B", +"U c #0E939D", +"Y c #06999F", +"T c #13929C", +"R c #04808F", +"E c #278698", +"W c #35899B", +"Q c #0E97A0", +"! c #019DA1", +"~ c #0D9BA2", +"^ c #1194A1", +"/ c #109CA6", +"( c #2999A4", +") c #3896A4", +"_ c #398EA0", +"` c #01A3A5", +"' c #0CA2A6", +"] c #0BA6A8", +"[ c #01ACAA", +"{ c #0BABAC", +"} c #02A8A8", +"| c #16A8AB", +" . c #01B3AE", +".. c #0AB1AF", +"X. c #0DAEB1", +"o. c #03B6B1", +"O. c #09B5B1", +"+. c #05BEB6", +"@. c #08BBB5", +"#. c #07BFB8", +"$. c #0ABBB9", +"%. c #19B9B6", +"&. c #1CAFB0", +"*. c #26A6AB", +"=. c #3BABB1", +"-. c #28B9B8", +";. c #37B9BA", +":. c #33A6AE", +">. c #56839C", +",. c #45869C", +"<. c #63819C", +"1. c #4997A7", +"2. c #569AAA", +"3. c #508BA1", +"4. c #688AA3", +"5. c #748DA6", +"6. c #6995AA", +"7. c #7A97AD", +"8. c #799DB1", +"9. c #58A8B4", +"0. c #46B9BC", +"q. c #4FABB4", +"w. c #68A8B6", +"e. c #77A8B7", +"r. c #6CB3BD", +"t. c #79B1BD", +"y. c #00C1B7", +"u. c #06C4BA", +"i. c #09C2BB", +"p. c #05CABE", +"a. c #0BC9BE", +"s. c #17C4BC", +"d. c #2EC2BE", +"f. c #57BEC1", +"g. c #79BAC3", +"h. c #6ABCC3", +"j. c #05CEC0", +"k. c #09C8C1", +"l. c #04D3C4", +"z. c #03D9C7", +"x. c #03DDC9", +"c. c #0AD7C8", +"v. c #18D8C9", +"b. c #18CCC2", +"n. c #26CBC2", +"m. c #38CAC5", +"M. c #28DBCD", +"N. c #38D6CC", +"B. c #37DED0", +"V. c #01E3CD", +"C. c #00E9CF", +"Z. c #0CE3CE", +"A. c #01E6D0", +"S. c #00EDD3", +"D. c #09EDD5", +"F. c #19EAD4", +"G. c #00F1D6", +"H. c #00F5DA", +"J. c #00F9DE", +"K. c #15E2CE", +"L. c #26E9D5", +"P. c #38E9D8", +"I. c #37F0DC", +"U. c #2DF0DA", +"Y. c #00FDE2", +"T. c #48CBC7", +"R. c #57C7C7", +"E. c #48D4CD", +"W. c #58DBD3", +"Q. c #4DD9D0", +"!. c #68C9CA", +"~. c #78C9CC", +"^. c #69D7D3", +"/. c #76DBD7", +"(. c #65D1CF", +"). c #44E5D7", +"_. c #5BE9DC", +"`. c #49F0DE", +"'. c #6DE3DB", +"]. c #56F2E1", +"[. c #7AEBE2", +"{. c #78F4E7", +"}. c #68F1E3", +"|. c #849BB1", +" X c #88A6B8", +".X c #97ACBE", +"XX c #89B8C4", +"oX c #96B6C5", +"OX c #9BAEC0", +"+X c #A7BBC9", +"@X c #86C6CC", +"#X c #98C4CE", +"$X c #97CCD2", +"%X c #87D9D8", +"&X c #97D9DA", +"*X c #84CED1", +"=X c #A7C2CE", +"-X c #B2C1CF", +";X c #A8CAD3", +":X c #B9CAD5", +">X c #A5D8DB", +",X c #B7D5DC", +"X>X>X>X;X;X*X[.`.r.n n z v v v v c x l p l x x c c v v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u i u X>X>X>X>X>X;X*X[.`.@.n n v v v v v c g E | S k f r l l l z z z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u i e X>X,X,X,X,X>X>X;X*X_.R n v v v v v v x e 0.`.`.V.p.;.H f e e p l l z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y , X>X,X,X,X,X>X>X;X*XI.L n v v v v n n x g V.`.[.[.[.[.[.(.p.;.S f r l z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u u y X,X,X,X,X,X>X>X;X*Xa.n n v v v n n n l A `.[.*X*X-X-X*X*X*X[.`.V.9.K z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u i u X,X,X,X,X,X>X>X-X[.%.n n n n n n n b p o.[.*X;X;X;X>X;X;X*X*X[.`.~.T z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y 0 X>X,X,X,X,X,X>X;X*XoXR L n n n n n n b g u.*X-X;X>X>X>X>X>X;X*X*X[.N.L n z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y X>X,X,X,X,X>X>X;X*XI.L L n n n n n n b g C.*X;X>X>X,X,X,X>X>X;X*X[.g.L n z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u i u X,X,X,X,X,X>X>X;X*Xh.L L n n n n n n l G [.*X;X>X,X,X,X,X>X>X;X*X[.2.n n z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y w X,X,X,X,X,X>X>X-X[.%.L n n n n n n b l o.*X;X>X>X,X,X,X,X,X>X;X*X]._ n v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y X>X,X,X,X,X,X>X;X*XoXR L n n n n n n b g j.*X;X>X>X,X,X,X,X,X>X;X*XE.I n v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y t X>X,X,X,X,X>X>X;X*XT.I L n n n n n n b k Z.*X;X>X,X,X,X,X,X>X>X;X*Xl.L n v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y ; X,X,X,X,X,X>X>X;X*Xh.L L n n n n L L x G [.*X;X>X,X,X,X,X,X>X>X;X*X4.n n v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u i u X>X,X,X,X,X,X>X>X-X[.%.L L n n n L L L l ;.*X;X>X>X,X,X,X,X,X>X;X*X[._ L n v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y q X>X>X>X;X;X;X;X*X*X*X*X].N.q.! d e e r p q ,.-X;X>X>X,X,X,X,X,X>X;X*XoX_ I L n L L L L K g j.*X;X>X>X,X,X,X,X,X>X;X*XE.Y L n v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X>X>X>X>X>X>X>X>X;X;X;X;X*X*X*X*X_.I.r.o.Z w D.;X>X>X,X,X,X,X,X,X>X;X*XW.R I L L L L L L K k Y.*X;X>X,X,X,X,X,X>X>X;X*Xl.L L n v v v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y q X>X,X,X,X,X,X,X>X>X>X>X>X>X;X;X;X;X*X*X*X*X$X}.=X>X>X>X,X,X,X,X,X,X>X;X*Xx.I I L L L L L L x J [.*X;X>X,X,X,X,X,X>X>X;X*X4.L n n v v v v v z z z z z z s s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X;X;X;X;X;X>X>X>X>X,X,X,X,X,X,X,X>X>X;X&.L L L L L L L L x ;.*X;X>X>X,X,X,X,X,X>X;X*X[.' L n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u y t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X>X>X@Xb l x x K L L L L k j.*X;X>X>X,X,X,X,X,X>X;X*XE.R L n n n n v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X:XW.g.;.H k k k b F k {.;X>X>X,X,X,X,X,X>X>X;X*XS.I L n n n n v v v v v z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y t X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X;X*X+XE.j.,.~ j A =X;X>X>X,X,X,X,X,X>X>X;X*X4.I L n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X;X;X*X*X*X*XXX}.;X>X>X,X,X,X,X,X,X>X>X;X#X{ I n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y t X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X;X;X;X>X>X>X,X,X,X,X,X,X,X>X>X;X|.R I n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X>X>X;XF.L L n n n n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X,X,X,X,X,X,X,X,X,X,X,X>X>X;X@.a x b b n n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X|.e.G g l c b n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 0 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X-X+XG...k g l b n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y t X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X;X*X*X(.w.A g l c c v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t = X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X-X*X*X'.u.A r l x c v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X*X*X].u.k r l c v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X*X*X_.q.g p l z v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 7 X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X*X[.C.W p l c v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X;X;X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X*X*X[.w.r a l z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X-X-X-X*X*X-X;X;X;X;X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X;X*X[.H.g a z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 0 X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;Xf.3.x.R..X+X*X*X*X*X;X;X;X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X*X*X(.k p z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i y t X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X$.{ { { $.3.f.F.{.[.*X*X*X;X;X;X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X-X*X_.W p z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t @ X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X-X|.{ ] _ ] { { { { $.3.h.R..X*X*X*X;X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X-X*X'.k p z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 0 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ._ ] _ _ _ _ ] { { { #.$.$.f.T.oX*X*X;X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X-X*X_.l a z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xs._ _ _ _ _ _ _ _ _ ] { { { { { =.l..X*X*X;X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X*XH.t z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X+X&.] _ _ _ _ _ _ _ _ _ _ _ _ ] { { { #.k.oX*X-X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X[.:.t z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.{ { _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ] _ { J.*X*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X'.l s z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XF.{ { _ _ _ _ _ _ _ _ ] _ _ _ _ _ _ _ _ _ _ _ y.oX*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.t.u z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xs.{ ] _ _ _ _ _ ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ ' .X*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X'.z z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X].&.{ ] _ _ _ ] ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ R R oX*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.:.u z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.#.{ _ _ _ _ ] ] ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ I @.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XD.s z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XF.{ { _ ' ] ] ] ] ] { { { ] ] ] _ _ _ _ _ _ _ _ R R _ n k.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X_.n z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xs.{ { ] ] ] ] { { { { { { ] ] ] _ _ _ _ _ _ _ _ R R R I T +X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.T z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.=.{ { ] ] ] { { { { { { { ] ] ] _ _ _ _ _ _ _ _ _ R R R K D.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.%.z z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.#.{ ] ] { { { { { { { { { { ] ] ] _ _ _ _ _ _ _ _ R R R K e.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.<.v v z z z z z z z s s s s s s s s s s s s u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 7 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.{ { { { { { { { { { { { { { { ] ] ] _ _ _ _ _ _ _ _ R R K +.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X<.n v v z z z z z z s s s s s s s s s s s s u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xs.#.{ { { { { { { { { { { { { { ] ] ] ] _ _ _ _ _ _ _ _ R U / *X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xe.n n v v z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t t X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.=.#.{ { { { { { { { { { { { { { ] ] ] ' _ _ _ _ _ _ _ _ R K +.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X<.n n v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u t X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.$.#.{ { { { { { { { { { { { { { { ] ] ] ] _ _ _ _ _ _ _ T K ,.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.>.n n v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t @ X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.#.#.{ { { { { { { { { { { { { { { ] ] ] ] _ _ _ _ _ _ _ T G j.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.%.n n v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t = X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.#.{ { { { { { { { { { { { { { { { { ] ] ] _ _ _ _ _ _ _ T J X-X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X]._ L n v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X#X3.#.{ { { { { { { { { { { { { { { { { { ] ] ] _ _ _ _ _ ) G ..*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X{.R L n v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 7 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X|.=.#.{ { { { { { { { { { { { { { { { { { { ] ] ' _ _ _ _ T k E.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XH.L L n v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;Xc.] { { { { { { { { #.{ { { { { { { { { { { ] ] ] _ _ _ ( A w.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.a.L n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;Xx.( Q ( ) ` [ [ { #.#.#.{ { { { { { { { { { { ] ] _ ) T D o.*X;X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.[ L n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;XOXI.u.O./ Q Q ` ` [ [ [ { { { { { { { { { ] ' ) ( J H r.*X-X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XE.R I n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X;X*X*X_.H.r.;.X./ Q Q ) ) ` ` ` ` ` ) ) ( J H W ,.{.*X;X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.y.I L n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u t X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X;X;X*X*X*X*X].(.H.u.q.;.^ ^ ~ ~ E E ~ o.r.G. X*X*X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X_._ Y L n n n n n v v v z z z z z z z s s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i u t @ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X;X;X*X*X*X*X*X*X[.]..X X XoX+X*X*X*X-X;X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X[.f.R I n n n n n n v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t = X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X;X;X;X;X;X;X-X-X*X*X*X-X;X;X;X;X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X;X*X X_ R L n n n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X>X>X>X;X;X;X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X[.%.R I L n n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i t - X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X*X[.k.R R L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X;X*X[.l.] _ I L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X-X*X[.l.{ _ Y L L L n n n n n n n n v v v v v v z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X*X*X].h.{ _ R L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X;X*X[.T.3.{ ] R I L L L L L n n n n n n n n n v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X;X;X;X;X;X;X;X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X*XW.s.#.{ _ R I I L L L L L L n n n n n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X-XQ.|.OX*X*X*X*X*X;X;X;X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X&X!.L.d.#.{ ] R R I I I L L L L L L n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i i t ; X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;XXX3.3.3.s.c.R..X[.*X*X*X-X;X;X;X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X%X{ L R _ _ R R R I I I I L L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i i t ; X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X-XK.&.=.=.&.=.3.3.d.c.R..X[.*X*X*X;X;X;X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;XJ.J K Y R R Y I I I I L L L L L L n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u i i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.$.#.#.#.#.&.&.=.=.3.3.f.F.}.+X*X*X*X;X;X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;XOX:.K U R R I I I I I L L L L L L n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i i i i t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X-X+X3.$.#.{ { #.#.#.#.$.$.&.=.=.3.6.c.W.+X*X*X;X;X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X*Xj.K K R R I I I I I L L L L L n n n n n n n n v v v v v v z z z z z z s s s s s s s s s s s s u u u i i i i i i i i i i i i i i i i i i i i i i u t = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.&.#.{ { { { #.#.#.#.#.#.#.#.$.$.=.=.5.J..X*X*X;X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X*XH.K K R R I I I I L L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i i i i i i i u t = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.#.#.{ { { { #.#.#.#.#.#.#.#.{ #.#.$.$.$.=.z.{.*X*X;X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X*X*XC.U K R I I I I I L L L L L L n n n n n n n n v v v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i i u q * s u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u u u u s s s s s s s s s s s z z z z z z z v v v v v n n n n n n n n L L L L L L I I K A Z.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.#.#.{ { { { { #.#.#.#.{ { { { { { { #.#.#.#.$.z.{.*X*X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X*XC.b K Y I I I I L L L L L L n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i u q + X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.3.#.{ { { { { { #.#.#.{ { { { { { { { { { { #.#.#.$.F.+X*X;X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X[.H.b P I I I I I L L L L L n n n n n n n n n v v v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.$.#.{ { { { { { { { { { { { { { { { { { { { { { { #.{ 2.{.*X;X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X[.e.b Y I I I I L L L L L L n n n n n n n n v v v v z z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.#.#.{ { { { { { { { { { { { { { { { { { { { { { { { { { { U.*X;X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X].T L Y I I I I L L L L L L n n n n n n n n v v v v v z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.#.{ { { { { { { { { { { { { { { { { { { { { { { { { ] { { _ R.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X-X*XD.L R I I I I L L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.=.#.{ { { { { { { { { { { { { { { { { { { { { { ] ] ] ] ] { ' R T.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X[.` L I I I I I L L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i t 8 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.$.#.{ { { { { { { { { { { { { { { { { { { { ] ] ] ] ] ] ] _ ] _ R oX*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.g.n I Y I I I I L L L L L n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i i i i t 7 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.{ { { { { { { { { { { { { { { { { { { { { ] ] ] ] ] _ _ _ _ _ ] Y <.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X(.I I I I I I L L L L L L L n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i u t ; X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.{ { { { { { { { { { { { { { { { { { { ] ] ] ] ] ] _ _ _ _ _ _ _ _ T .X-X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.[ L I I I L L L L L L L L n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s u u u u u i i i i i i i i i i i i i i u q = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.=.{ { ] { { { { { { { { { { { { { { ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ P g.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.e.n I L L L L L L L L L L n n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.#.{ ] ] { { { { { { { { { { { ] ] ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ Y +.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xg.L I L L L L L L L L L n n n n n n n n n n n v v v v z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.{ { ] ] ] { { { { { { { { { ] ] ] ] ] ' _ _ _ _ _ _ _ _ _ _ _ _ _ _ T Q #X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XD.I I L L L L L L L n n n n n n n n n n n n n v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xf.{ { ] ] ] ] { { { { { { { ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ Y W +X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XI.I I L L L L L n n n n n n n n n n n n n n n v v v v v v z z z z z z z s s s s s s s s s s u u u u u i i i i i i i i i i i i t 7 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.=.{ ] ] ] ] ] { { { { { ] ] ] ] ] ' _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R T W +X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XE.I L L L L n n n n n n n n n n n n n n v v v v v v v v v z z z z z z z s s s s s s s s s s s u u u u i i i i i i i i i i i u q ; X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X{.#.{ _ _ ] ] ] ] { ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R K X.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XE.I L n n n n n n n n n n n n n n n n v v v v v v v v v z z z z z z z z s s s s s s s s s s s s u u u i i i i i i i i i i i t q @ X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.{ { _ _ _ ] ] ] ] ] ] ] ] ' _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R x q.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XD.R L n n n n n n n n n n n n n n n v v v v v v v v v z z z z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i t q X>X>X>X>X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xs.{ ] _ _ _ ] ] ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R R R T k G.*X;X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XS.I L n n n n n n n n n n n n n v v v v v v v v v z z z z z z z z z z z z s s s s s s s s s s s s u u u u i i i i i i i i i t q X>X>X,X,X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X#X&.{ _ _ _ _ _ ] ] ] ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R R R R K A oX;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*Xh.L L n n n n n n n n n n v v v v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s u u u i i i i i i i i i t 7 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X|.{ ] _ _ _ _ _ ] ] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R R R R R U k u.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-X[.2.L L n n n n n n n n n v v v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s u u u u i i i i i i i u q = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;Xc.R _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R R R R R R T k D +X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.' L n n n n n n n n v v v v v v v z v v z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s u u u u i i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;Xf.K G G U ) ) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ R R R R R R R R R R R U A j {.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X{.R L n n n n n n v v v v v v v v v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u i i i i i i t q X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X-XXXH.w.X.J J J T ) ) ) _ _ _ _ _ _ _ _ R R R R R R R R R R R Y K k D Y.*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XJ.L L n n n n v v v v v v v v z z z z z z z z z z z z z s z s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i u t 7 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X-X*X#X(.A.q...H J J U U T T T T R R R R R R R R R Y Y U K k A ;..X*X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X-X[.4.L n n n v v v v v v v v z z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i t q * X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X;X;X*X*X*X[.(.H.u.,.^ J D G A J K K U U U U K k k k A E w.Y.*X*X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X]._ L n v v v v v v v v v v z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i t q X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X;X;X;X-X*X*X*X*X[._.N.A.u.;.;...E E E E ..;.q.j.I.+X*X*X;X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*XH.I L n v v v v v v v v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i t 8 X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X;X;X;X;X-X*X*X*X*X*X*X*X+X+X+X+X*X*X*X*X*X;X;X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.1.L n v v v v v v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i u q ; X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X;X;X;X;X;X;X;X;X;X;X;X;X;X;X;X;X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X XR L n v v v v v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i t q X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X-X[.a.L n v v v z z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i t 8 X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X]._ L n v z z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i u q ; X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X;X*X[.a.L n v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X_.R L n z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i t 8 X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X[.2.L n z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u s u u u u u u u u i i i i i i i i i i i i i i i i i i t q = X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X*X[.D.L L v z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X-X*X XR L n z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i u q 7 X>X,X,X,X,X,X,X,X>X>X>X>X;X;X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X;X-X*X'._ I n z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u i u u i i i i i i i i i i i i i i i i i i i i i i t q o X>X,X,X,X,X,X,X>X>X>X=X;X-X-X-X;X;X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X;X-X*X].%.L L z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X>X,X,X,X,X,X>X>X;X=X=.5.c.W.oX*X*X-X;X>X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X*X*X_.%.I L z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i t q - X>X,X,X,X,X,X>X>X;X|._ _ _ { #.4.l.}.$X;X>X>X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X;X;X*X[.E.{ I L v z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u i u i i i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X,X,X,X,X,X>X>X;X*XF.R R R R _ _ { { { 4.-X>X>X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X>X>X>X>X,X,X,X,X,X,X,X,X,X,X,X>X>X>X>X>X>X>X;X;X*X*X[.k._ I n z z z s s s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q 7 X,X,X,X,X,X>X>X;X*X4.R I I I I R R R b U -X>X>X,X,X,X,X,X,X,X,X>X>X>X>X>X;X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X>X;X;X;X-X*X*X[.T.*.R L n z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q X>X,X,X,X,X,X>X;X*X+X] R I L I I I I P x t.;X>X>X,X,X,X,X,X,X,X>X>X;X;X;X;X-X-X-X;X;X;X;X;X;X;X;X;X;X;X;X;X;X;X;X;X;X*X*X*X[.].U.4.R I L v z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q 7 X>X,X,X,X,X>X>X;X*XE.R Y L L I I I I K k I.-X;X>X,X,X,X,X,X,X>X>X;X|.f.J.W..X[.[.*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X[._.I.h.#.R L L n z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u i u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q . X,X,X,X,X,X>X>X;X*Xl.I I L L L I I P K A oX-X>X>X,X,X,X,X,X>X>X;X;Xs.R _ _ { #.4.y.S.l.T.{.{. XoXoXoXoX].oX{.{.E.k.a.2.{ _ I L n v z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q 7 X,X,X,X,X,X>X>X;X[.2.I I L L L L I L x ^ *X;X>X>X,X,X,X,X,X>X>X;X*X#.I I I I Y I R I _ R _ ] { { [ { { { { ] _ R R I I L n n v z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q . X>X,X,X,X,X,X>X;X*X]._ Y L L L L L I L k r.*X;X>X>X,X,X,X,X,X>X;X-X.XR L n n n n n n L L L L L L L n L n n n L n n n c v z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q 7 X>X,X,X,X,X>X>X;X*XT.R I L L L L L L K k H.*X;X>X>X,X,X,X,X>X>X;X*XJ.L L n n n n n v v v v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X,X,X,X,X,X>X>X;X*Xk.I I n L L L L L b k ].*X;X>X,X,X,X,X,X>X>X;X*Xy.L n n n n v v v v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q 7 X,X,X,X,X,X>X>X-X[.2.L L n L L L L L l ^ [.-X>X>X,X,X,X,X,X>X;X*X[.[ L n n n v v v v v v v z z v z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X,X,X,X,X,X>X;X*X]._ L L n L L L L K g r.*X;X>X>X,X,X,X,X,X>X;X*X{.R L n v v v v v v v z z z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q - X,X,X,X,X>X>X;X*XE.I L n n n L L L b g H.*X;X>X>X,X,X,X,X>X>X;X*XF.L L v v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i u q 8 X>X>X,X>X>X>X;X*Xk.L L n n n n L L x k _.*X;X>X,X,X,X,X,X>X>X;X*Xy.n n v v v v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q # X>X>X>X>X;X*X[.2.L L n n n n n b l ~ [.-X>X>X,X,X,X,X,X>X;X*X[.' L n v v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s s u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q 7 X>X,X,X,X,X,X>X;X*X{.I n c v v z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X>X,X,X,X,X>X>X;X*XF.L n v z z z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q - X>X,X,X,X,X>X>X;X*X4.n n z z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q 7 .L n n n n n n b l E [.*X;X>X>X,X,X,X>X>X;X*X[.' n v z z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t 8 X>X>X>X>X>X;X*X{.I n z z z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q - X>X>X;X;X*X[.S.n n z z z z z z z z s s s s s s s s s s s s s s s s s s s s s u u u u u u u u i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i i t q 7 XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX>XF U U Y ^ Q ~ ~ Y ~ ~ ~ ~ ~ ' ' ~ ' ' ' ' ] ' ' ] ] ] ] ] ] { ] { { { { { { ....{ ......O.O...O.O.O.O.O.@.O.@.@.O.@.i.@.y.y.@.@.i.@.y.u.u.i.i.u.u.u.u.p.u.p.a.a.p.p.p.a.l.p.p.l.p.l.j.p.l.l.l.l.l.z.l.8XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXfX`.C.S.C.S.G.S.S.S.S.S.S.S.S.S.S.G.S.S.G.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.G.C.S.S.C.G.S.S.S.S.S.S.Y.MXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXp z p p k k z a k k k k k k k k l k l l l l l l l c c J l c J c d 9.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXyXU I U U U ~ U ' U ~ ~ ~ ~ ~ ' ~ ' ~ ' ' ' ' ' ] ' ' ] ] ] ] ] ] ] { { { { { ..{ { ....................@.O.@.O.@.@.@.O.@.@.@.i.O.u.u.y.u.i.u.i.y.s.u.u.u.p.p.u.p.p.p.p.p.p.p.l.p.l.p.l.l.l.l.l.l.l.l.p.).mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX8XL.C.C.A.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.G.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.C.S.S.S.S.S.S.S.S.J.S.MXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXz p p p p k p k p p k k k k k k k l k l k l l l l l c l c J l h S yXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXkX*.F U U U U U U U ~ ~ U ~ ~ ~ ~ ~ ~ ' ~ ~ ~ ' ~ ' ' ] ] ' ] ] ] ] { ] { { ] { { { ..{ ............@...@...O.O.O.@.O.@.@.@.@.@.O.i...i.i...y.u.y.i.y.i.u.u.u.u.u.p.u.p.p.p.p.p.p.l.p.l.p.l.l.p.p.l.l.z.x.dXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXqX`.C.C.C.A.S.C.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.G.S.S.S.S.S.S.S.S.S.S.G.G.S.G.S.S.S.S.S.S.G.G.MXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXp z p p p p p p p k k k k k k k k k k l l k l l l l l l l l J J d 9.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX9.F T T U U U ~ U ~ U ~ ~ ~ U ~ ~ ~ ~ ~ ' ~ ' ' ~ ' ] ~ ' ' ] ] ] ] ] ] { ] ] { { { { ..{ ..............O.O.O.O.O.O.O.O.@.@.O.O.i.O...u.i.y.i.u.u.i.i.y.u.u.u.u.u.u.u.p.p.u.a.p.p.p.j.p.p.p.l.l.l.l.p.p.2XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXxXzXzXqX[.P.V.V.C.A.A.A.S.S.S.A.S.S.S.S.S.S.S.S.S.S.G.S.S.S.S.G.S.S.S.S.S.S.G.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.Y.S.MXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXp a p p p p p p p p k p k k k k k l l k k l k l l l l l J l l h l iXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX$XD G U U U U U U U U ~ U U ~ ~ ~ ~ ~ ~ ~ ~ ' ~ ' ' ' ' ] ~ ] ' ' ] ] ] ] ] { { { { { { { { ....................O.O.O.O.@.O.@.i.i.O.y.i.i...u...i.y.i.y.u.i.u.u.u.u.u.p.u.p.a.p.p.p.p.p.p.l.l.p.p.p.l.p._.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXzX0X[._.).L.K.Z.Z.V.z.V.C.V.C.C.A.C.C.A.S.C.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.G.C.S.G.C.G.G.S.S.S.S.S.S.S.S.S.S.S.G.S.S.H.S.MXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXp a p p p p p p p k p p k k p k p k k k k k k l l l k l l l l l d w.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXyXG H G G U U U U U U U ~ U ~ U ~ Y ~ ~ ~ ~ ~ ~ ~ ~ ' ~ ' ~ ' ' ' ] ' ] ] ] { ] ] { { ] { { { { { ..{ ..........O.O.O.O.O.O.@.@.O.O.i.O.y.O.i...u.O.u.u.s.y.i.i.u.u.u.u.p.u.u.p.p.p.u.p.p.p.p.j.p.l.p.p.).nXmXmXmXmXmXmXmXmXmXmXmXmXfX2X).c.l.z.x.x.x.x.V.x.V.V.V.V.V.A.V.A.A.A.A.S.S.S.S.S.S.S.S.S.G.S.S.S.S.S.S.S.S.S.S.G.S.S.S.S.S.S.S.S.S.S.S.G.C.S.C.G.S.S.S.S.S.S.S.G.J.MXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXu a p u p p p p p p p p k k k k k k k k k l k k k l k l l l l h v uXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXq.A U G G T U G U U U U U U U ~ ~ U ~ ~ U ~ ~ ~ ~ ~ ~ ' ' ' ' ' ' ' ] ] ' ] ] ] ] ] { { { { { { ..{ { ............O...O.O.O.O.O.i.O.O.O.O.i.i.O.u.u.O.u.y.i.O.i.u.u.u.u.u.u.p.u.a.a.p.p.p.p.p.l.l.p.p.N.xXmXmXmXmXmXmXmXmXmXmXmXqX_.c.z.z.z.x.x.x.x.V.x.x.V.V.V.V.V.V.V.C.V.C.C.A.A.C.A.C.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.G.G.S.S.S.S.S.S.S.S.S.J.S.MXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXi p u u p p p p p p k p k p p k k k k k k k l k l l l l l l l s 9.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX$XA G G G G U U U U U U U T U U U U ~ U U ~ ~ ~ ' ' ~ ~ ' ~ ' ' ' ' ' ' ] ] ] ] ] ] ] { ] ] { { { { ..[ ....{ ......O.O.O.O.O.O.O.O.O.y.i.y.i.O.i.O.y.u.O.O.u.u.u.u.u.u.u.u.u.u.p.p.p.p.p.p.p.p.p.p.y.E.zXmXmXmXmXmXmXmXmXmXmXsX_.l.z.z.x.x.z.x.x.x.x.V.x.V.V.x.V.V.V.V.V.V.C.V.A.C.A.S.A.C.S.S.S.S.S.S.S.S.S.S.G.G.S.S.S.S.S.S.S.G.S.S.S.S.S.G.S.S.S.S.S.S.S.S.S.S.G.S.S.S.S.H.S.MXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXu a p u u p p u p p p p p p p k k p k k k k k k k k k l k l l l l 1XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXuXT A L G G G G G T T U U U Y U Y U ~ U ~ ~ ~ ~ U ~ ~ ~ ~ ~ ' ~ ' ~ ' ' ' ' ' ' ] ] { ] ] { { { { { { { { { ................O.O.O.O.O.O.O.O.O.O.y.O...i.O.u.u.u.u.O.u.u.u.u.u.i.p.u.u.p.u.p.p.p.p.p.p._.xXmXmXmXmXmXmXmXmXmXnX[.l.p.l.z.l.z.z.z.z.x.x.x.x.x.x.x.V.x.V.x.V.V.V.V.V.A.A.V.C.C.A.A.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.C.G.S.S.G.C.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.G.J.MXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXu p p q p u p p p p p p p p p k k p p k k k k k k k k h k l l r 2.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX9.A L L L L G G G G G U U T U U U U U U U ~ ~ U U ~ ~ ~ ~ ~ ' ~ ' ~ ' ~ ' ' ' ] ' ] ] ] ] { ] { { { { { { { { ......{ O.O.O...O.O.O.O.O.@.O.O.@.@.y.i.O...u.y.y.s.y.#.u.u.u.u.u.u.u.u.u.p.u.p.p.p.a.XcXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXxXN.p.p.p.p.p.l.p.l.p.l.l.l.l.l.l.z.l.l.z.z.z.z.z.x.z.x.x.x.x.x.x.V.V.V.V.V.V.V.V.V.V.A.A.A.A.C.A.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.G.S.G.S.S.S.S.S.S.S.S.G.S.S.S.S.S.S.S.Y.MXMXMXMXMXMX", +"MXMXMXMX4 i 4 4 q q q p q q q q p u p q p p p p p p p p k k p p p k t m kXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX#Xg J J J J J K K J K K L L L L L G G G T T U U U U U U U U ~ U U ~ ~ ~ U ~ ~ ~ ~ ~ ~ ' ~ ' ~ ' ' ] ` ! 0.6XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX'.p.p.l.p.l.p.p.l.p.l.l.l.l.l.l.l.l.l.c.l.z.z.z.z.z.x.x.x.x.x.x.V.x.V.x.V.V.V.V.A.A.C.C.V.C.C.C.C.A.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.Y.S.MXMXMXMXMX", +"MXMXMXMX4 w 4 u 4 4 u q u u q u p q p p q p p u p p p p p p p p k k 0 w.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnX) g J J J J J K K K K K K K K L L L L G G G G G T U U U U U Y U U ~ ~ ~ ~ ~ U ' ~ ~ ~ ' ~ ~ ' ' ' Y *.>XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXpXu.y.p.p.p.p.p.p.z.p.p.p.z.z.l.p.l.z.l.z.l.z.z.z.z.z.z.z.V.z.V.x.x.x.V.V.x.V.V.V.V.V.V.C.F.L.U.L.D.C.C.C.S.S.S.S.S.S.S.S.G.S.S.G.G.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.H.S.MXMXMXMXMX", +"MXMXMXMXi 4 4 4 4 u 4 q q q q u q q q p u p p p p p p p p p p z p k t ;XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX,Xg c J J J J J J J J K K K K L K L L L G G G G G U U U U U U U U U U U ~ U ~ ' U ~ ~ ~ ' ~ ' ' Y Y !.xXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXQ.y.p.p.a.p.p.p.p.p.p.z.z.p.p.z.z.l.p.l.l.c.z.l.l.z.z.z.z.z.z.z.x.x.x.x.x.V.x.V.V.x.Z.}.0XfXxXxXxXfX8X`.A.C.S.S.S.S.S.S.S.C.S.S.C.S.S.S.S.G.S.S.S.S.S.S.G.S.S.S.S.S.S.S.G.G.G.MXMXMXMXMX", +"MXMXMX4 i 4 4 4 4 w q q 4 u u q q p q q q p q q q p p i p p p p p p m kXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX2.d J J J J J J J J K J K K K K K L L L L G G G U U L G G G U U T U U Q U U ~ U ~ U ' ~ ~ Y ` Y *.6XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXpXu.p.p.p.p.p.p.p.z.p.p.p.p.z.p.p.c.p.z.l.l.l.l.z.l.c.z.z.c.x.z.z.x.x.x.x.x.x.V.x.x._.fXmXmXmXmXmXmXmXmXmXqXI.S.S.S.S.S.S.G.S.S.S.S.S.S.S.S.S.S.S.G.S.S.S.S.G.S.S.G.S.S.S.S.S.Y.MXMXMXMXMX", +"MXMXMX5 w 5 4 4 4 4 4 q q q q q q q p p p u p p p p p p p p p p p 0 2.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXyXJ h c l J l J J J J J J K J K K K K L L L L G G L G U G U T U U Y ~ U U U ~ ~ ~ U ' U ~ ~ ~ Y =.gXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXQ.y.u.u.p.p.p.p.p.p.p.p.p.p.p.z.p.p.z.p.l.l.l.l.l.z.l.z.z.z.z.z.x.z.x.x.x.x.V.x.x.4XmXmXmXmXmXmXmXmXmXmXmXmXnX_.C.S.S.S.S.S.S.S.S.G.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.C.G.S.S.S.J.S.MXMXMXMX", +"MXMXMX5 4 5 4 4 4 4 4 4 4 q i q q q q q q p p p p q p p p p p p p 0 oXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXXXd c J c c c J J J J J J J J K J K K K L L L L G G G G U T G U U U U U U U Q U U ~ ~ ~ U ~ F r.nXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXdXu.u.u.a.u.p.a.p.p.p.p.p.p.p.l.l.p.p.z.p.z.l.l.l.l.l.z.z.c.z.l.c.x.z.x.x.x.x.x.z.4XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX`.C.S.S.S.S.S.S.S.C.S.S.S.S.G.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.H.S.MXMXMXMX", +"MXMXMX4 5 5 5 5 4 4 4 4 q 4 4 q q q q p q q q q q p q p u p p p p k iXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnX_ f c c c c J J J J l J J J K J K K J L L L L L L G G G G G G U G T U U U U U U U ~ U ~ Y Y ~.nXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX/.y.u.u.u.u.p.p.u.p.p.p.a.a.p.p.a.l.z.p.l.a.l.l.l.l.l.l.l.l.z.z.z.z.Z.z.z.x.x.z._.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXxXL.C.S.S.S.S.S.S.S.G.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.H.S.MXMXMXMX", +"MXMXMXw 5 5 5 5 4 4 4 4 4 q q q q q q p p q q p p q p p p p p p 9 W bXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX,Xg h l c c J c l J J J J J J J K J K K K K K L L K G G G G G G T T U U U U U Y U U U ~ Y I @XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXd.y.u.u.u.u.u.p.u.p.p.u.p.p.p.p.l.p.l.p.p.z.l.p.l.l.l.l.l.c.l.z.z.z.z.z.z.z.z.c.dXnXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX8XS.S.S.S.S.S.G.G.S.S.S.S.S.S.S.S.S.G.S.S.S.S.S.S.G.S.S.S.S.G.H.MXMXMXMX", +"MXMX3 w 5 5 5 5 5 5 4 4 4 4 4 4 q q q q q q q p q p q p q p u p 0 e.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXr.s h l l c l c J l J J J J J J J K J J L J L J L L L L G G G G G U U U L U U U U U ~ U I !.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXfXa.y.u.u.u.i.u.p.u.p.p.p.p.p.p.l.p.p.p.p.l.p.l.l.p.l.l.l.l.l.z.l.z.l.z.z.z.z.x._.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXL.C.A.S.C.C.S.S.S.S.S.S.S.S.S.S.G.S.S.S.S.S.G.S.S.S.S.S.S.S.Y.MXMXMXMX", +"MXMX3 5 3 3 5 5 5 5 4 4 4 4 4 4 q q 5 q q q q q z q q q q q z q 9 ;XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXW j l l J l J l J J l l J J J J J J J L J L K K L K L L L L L G G G G G U G U U U U U I q.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX3Xo.y.i.i.u.u.u.u.u.p.a.p.p.u.p.a.p.p.p.l.p.p.l.l.l.p.l.l.l.l.l.l.z.z.l.z.z.z.x.8XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX}.C.A.S.S.S.C.S.S.S.S.S.S.S.S.S.S.S.S.S.S.G.S.G.S.S.S.S.S.S.J.S.MXMXMX", +"MXMX3 5 3 5 3 3 5 4 5 5 5 4 4 4 q q q q q q q q q q p z z z q 9 p iXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXwXf l l l l l l l l l J J J J l J J J J J J J K K J K K L L G L G G G G U G U U U U U F :.kXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX^.y.y.y.i.u.i.u.u.u.u.u.u.p.p.p.p.p.p.l.p.p.l.p.l.p.l.p.l.l.l.l.l.l.c.z.z.z.l.c.fXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX0XC.V.C.S.S.S.A.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.J.S.MXMXMX", +"MXMX3 3 3 3 3 3 5 5 5 5 5 4 4 4 q 5 5 q q q q q q q 9 q q q q 9 ,.bXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXt.r l l l l l l l J l J J l l J J J J J J K J J K K K K K L L L L G G G G U G U U G I U 7XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXT. .i.i.u.u.i.u.u.u.p.u.p.u.a.p.p.p.p.p.p.l.p.p.l.l.l.l.l.l.l.l.l.z.l.z.z.z.l.M.xXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXfXD.C.C.S.C.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.G.G.S.MXMXMX", +"MXMX5 3 3 3 3 3 3 5 5 5 5 5 4 4 5 q q q q q q q q q q q z q q 0 6.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX1.r l k l l h l l l l l l J l J J J J J J J J L J S K K K K L L L L L G G G G G G U D @XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXd.y.i.i.O.u.y.u.u.u.u.u.u.u.a.a.p.p.p.p.p.p.p.p.p.l.p.p.l.l.l.l.l.l.l.l.l.l.z.M.nXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXU.C.A.A.A.C.S.A.S.S.S.S.S.S.G.S.S.S.S.S.S.S.S.S.S.S.S.S.S.G.S.MXMXMX", +"MX2 5 2 3 2 3 3 3 3 3 4 5 5 4 4 q q 5 q q q q q q q p q q q z 9 XXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXgXv f l k l l l l l l l l l J l J l l l J J J J J K J K K J K K L L L L G L G G U G D :.nXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXkXs.y.O.i.i.y.u.u.u.u.u.u.u.u.u.u.p.p.p.u.p.p.p.p.l.p.l.j.l.l.p.l.l.l.l.l.z.l.l.v.xXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX_.V.C.A.S.A.S.S.S.S.S.S.S.S.S.S.S.S.S.G.S.S.S.S.S.S.S.S.S.G.H.MXMXMX", +"MX2 w 2 2 2 3 3 3 3 3 3 5 5 5 4 5 5 q 5 q q q q q q q q q q q 9 :XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX;Xj k k l l k l l l l l J J l J l J J J J J J J K K J K J K K K K K L L G G G G G G F ,XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlXs.y.i.y.O.y.s.u.u.u.u.u.u.u.u.a.u.u.p.p.p.p.p.p.p.p.l.p.p.l.a.p.l.l.l.l.l.z.l.z.sXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX4XV.V.C.A.A.C.C.C.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.G.S.S.S.S.S.Y.MXMXMX", +"MX2 2 2 2 3 3 2 3 3 3 3 3 5 5 5 q 5 5 q 5 q q q q q q q q q q q iXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXe.s k k k k l l k l l l l k J l J J l l J J J J J J J K K K K K K K K L L L L G G A q.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXkXs.y.O.O.u.i.O.y.i.u.u.u.u.u.i.p.p.p.p.u.p.p.a.p.p.p.p.l.p.l.l.l.l.l.l.l.l.l.l.l.3XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX8XC.V.C.A.A.A.A.A.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.Y.S.MXMX", +"MX: 2 2 2 2 2 3 3 3 3 5 3 3 5 5 5 5 5 5 q 5 q q q q q q q q 9 M nXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX1.0 k l k l k l l k l l l l l l J l J l J J J J J J J J J J J K K K L L L G L U H H yXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlXs.O.y.O.i.y.u.O.i.y.u.u.u.u.y.u.u.u.u.a.p.p.p.p.p.p.l.p.j.p.l.p.l.p.l.l.l.l.l.l.'.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXfXV.V.A.C.V.A.C.S.A.S.S.S.C.S.S.S.S.G.S.S.S.S.S.S.G.S.S.S.S.J.S.MXMX", +"MX: 2 ; 2 2 2 2 3 2 3 3 3 3 5 5 5 5 5 4 4 4 4 4 q q q q q q < 3.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlXm t k k k k k l k l l l l l l l l J l J J l J l J J J J J J J J K K K K K K L L D q.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXkXs.o.@.@.@.@.i.i.@.i.i.i.i.y.i.u.u.u.p.p.p.u.p.p.a.p.p.p.p.p.l.p.l.l.p.l.l.l.l.p.).mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXxXF.V.V.V.A.V.A.A.S.A.A.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.G.G.S.J.S.MXMX", +"MX; 2 2 2 2 2 2 2 3 2 5 3 3 5 5 5 5 5 5 5 4 4 4 q 5 5 q q q < w.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXrXk k k k k k l k l k l k l l l l J l l l l J J J J J J J J J J J K J K K K K L L A >XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXxXd.o.@.@.@.@.i.@.y.y.@.i.y.i.i.u.u.u.u.u.u.p.p.p.p.p.p.p.l.p.p.l.p.l.l.j.l.l.l.p.v.xXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXP.x.C.V.C.A.C.C.A.C.S.C.A.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.G.S.MXMX", +"MX2 : ; ; 2 2 2 2 2 2 3 3 3 2 5 2 5 5 5 5 4 4 4 q q q q q q * .XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX=Xr k k p k k k k k l l l l l l l l J l l J l J l J J J J J J J J K J K K K K K H ( nXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXT. .@.@.@.@.@.y.@.@.i.i.i.i.i.u.u.u.p.u.u.u.p.p.p.p.p.p.p.p.p.l.p.p.l.l.j.l.l.l.l.sXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX_.V.V.V.V.V.A.C.V.A.S.S.S.S.S.A.S.S.S.S.S.S.S.S.S.S.S.S.S.H.S.MXMX", +"MX2 ; 2 ; 2 ; 2 2 2 2 2 3 2 5 2 5 5 5 5 5 5 5 4 5 q 5 5 q q 6 =XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXt.0 k p k k k k k l k k z l l l l l l l l J l J J l J l J J J J J J J K K S K K A g.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX(. .@.@.@.y.@.@.@.i.@.@.i.y.y.u.i.u.u.u.u.u.p.p.p.p.p.p.p.p.p.p.l.p.p.j.l.p.l.l.p.3XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX[.x.V.V.V.V.C.A.V.C.C.C.A.A.C.S.S.S.G.S.S.G.S.S.S.S.S.S.S.G.S.MXMX", +"MX2 2 - 2 ; 2 2 2 2 2 2 2 2 2 5 5 5 2 5 5 5 4 4 q 5 q q 5 q q eXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX2.y k p p k k k k k k k k l l l l l l J J l l l l J l J J l J J J J L J K J K H A rXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX&X .@.@.O.@.@.@.@.i.@.i.@.@.i.u.u.i.u.a.u.u.u.u.u.p.p.a.p.p.p.p.p.l.p.l.p.l.p.l.p.[.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX4Xx.V.V.V.A.V.A.A.V.A.A.S.A.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.S.G.MXMX", +"MX7 ; - ; 2 2 - ; 2 2 2 2 5 2 2 2 2 5 5 5 5 5 5 5 5 5 q q 5 q iXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX3.t p k k k k k k k k k l k k k k l l k l l J l J l J J J J J J J J J J J K K S ( vXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXpX@.O.@.@.@.@.@.@.@.@.@.y.i.@.u.u.i.i.u.u.u.u.p.a.p.u.p.p.p.p.l.p.j.p.p.l.l.p.l.p.'.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX8XV.V.x.V.x.A.V.C.C.V.C.A.C.S.A.S.C.S.S.S.S.S.S.S.S.S.S.S.G.H.MXMX", +"; 2 ; ; ; 2 ; 2 2 % 2 2 2 2 2 5 2 5 6 5 2 5 5 5 5 q q 6 5 6 q lXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlXM y k p k p k k k k k k k k k l k l l l l l l l l l J l l J J J J J J J J K K R w.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXbXd. .O.@.@.@.@.@.i.@.@.@.i.i.@.y.i.y.u.u.u.u.u.p.u.p.p.p.p.p.a.p.p.p.p.j.j.l.p.p.Q.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXqXx.V.V.V.V.V.V.A.V.C.V.A.A.A.S.A.S.C.S.S.S.S.S.S.S.G.S.S.S.Y.MXMX", +"= : ; ; ; ; ; ; 2 2 2 2 2 2 2 2 2 6 5 2 5 5 5 5 5 5 6 q q 6 M vXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXjXz t p k p k p k k k k k k k l z l l k l l l l J k J l J l J J J J J J J J J J R @XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX(. .@.O.O.@.@.@.O.y.i.@.@.y.i.i.@.i.i.u.u.u.u.u.u.u.u.p.u.p.p.p.p.l.j.j.l.p.l.p.M.nXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXdXV.x.V.x.V.V.x.V.V.V.C.A.V.C.C.S.S.S.S.S.S.S.S.S.S.S.S.S.S.Y.MXMX", +"= ; ; ; ; ; ; 2 ; ; ; 2 2 2 2 2 2 5 2 5 2 5 5 2 4 5 5 4 6 > ,.bXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXyXp p p k p p k p k p k k k k l k k l l l l l l l J l l l J J J l J J J J J J J S yXnXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX%X .o.O.@.O...i...s...i.y.u.@.y.i.@.y.y.i.i.u.u.u.u.u.p.p.p.p.p.p.p.p.l.l.p.p.p.v.xXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXzXc.V.V.V.x.x.V.V.V.V.V.A.C.V.A.A.A.S.S.S.S.S.S.S.S.S.S.S.S.Y.MXMX", +"= ; = ; % ; ; ; ; 2 2 ; 2 2 ; 2 2 2 2 2 5 2 5 5 5 5 5 5 q > >.nXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX,Xu p p p p k p k k p k k k k k k l k k l k l l l l l l J l l l J l J J J J J l P jXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXyX .O.O.O.@.y...y.y.i.i...O.i.@.i.@.i.i.i.i.u.u.u.u.u.u.u.p.u.p.p.p.p.p.p.p.z.p.a.zXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXxXK.x.x.x.V.V.V.V.V.V.A.V.A.A.C.A.C.A.A.S.S.S.S.S.S.S.S.G.S.Y.S.MX", +"= ; ; = % ; ; ; ; 2 ; 2 ; 2 2 2 2 2 2 5 2 5 2 5 2 5 5 5 q & >.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX;X9 p p p p k p k k k p k k k k k h k k l k l l l l l l l l J J l l J J l J J h ) vXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXkX..o.O.O.@.....y.....y.u.i.@.i.@.y.i.y.y.i.i.i.u.u.u.u.p.p.p.a.a.p.p.p.p.p.p.p.z.dXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXxXM.z.V.V.x.V.x.V.x.A.V.V.C.A.C.A.A.A.A.A.C.S.S.S.S.S.S.S.S.Y.S.MX", +"= = = ; ; ; ; ; 2 ; ; ; ; % 2 2 2 2 2 2 2 5 2 5 5 6 5 5 5 * 4.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX=X9 t p p p p p p p p k k k k k k k k k l l l k l l l l l l l l J J l J J J J g q.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXxX-. .O.O.O...i.O...i.....i.@.@.@.i.O.u.O.i.i.u.u.i.u.p.u.u.p.p.p.p.p.p.p.p.p.p.p.qXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXL.z.x.x.V.x.x.V.V.V.V.V.V.V.V.A.C.S.C.C.S.A.A.S.S.S.S.S.S.Y.S.MX", +"% $ $ ; ; ; ; ; % ; 2 ; ; 2 2 2 2 2 2 2 2 2 2 2 2 5 5 5 5 * 6.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXXX, u u p p p p p p k p k k k k k k k k k k l l l l l l J l l l l J l l J l J g 9.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXvXd. ...........i...i...y...O.i.O.y.u.u.i.y.u.#.u.u.u.u.u.u.u.p.p.p.p.p.p.p.p.p.p.pXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXL.x.z.x.x.x.V.x.V.V.V.V.V.V.V.A.V.C.A.A.S.S.C.S.S.A.S.S.S.J.S.MX", +"$ $ $ $ $ $ $ ; ; ; ; ; ; ; ; 2 2 2 2 2 2 2 3 2 2 5 2 5 2 * 4.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXoX, p p p p p p p p p p k k q k k k k k k k k k l l k k l l l J l l J J l l J d r.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXd. .{ i.....i.y.....i...i.i...i.i.i.O.u.O.u.@.@.u.i.u.u.p.u.u.u.u.u.p.p.p.p.p.p.8XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX).z.z.x.x.x.x.V.x.V.x.V.V.V.V.C.V.C.C.A.A.C.A.S.S.S.S.S.S.J.S.MX", +"$ $ $ ; ; ; $ ; ; ; ; ; ; ; ; 2 2 2 2 2 2 2 2 3 2 2 5 5 5 * 6.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXXX0 p u u p p p p p k p p k z k z p p k k k l k k k l l l l l l l l l l J J J 0 r.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXbXT.} ....y.i.......y.....i.O.y.i.O.y.i.i.i.#.u.#.u.u.u.u.i.u.u.p.u.p.p.p.p.p.p.p.6XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX).p.x.x.x.x.x.V.x.V.x.V.x.A.V.V.A.V.V.A.A.A.C.S.C.S.S.S.S.J.S.MX", +"$ = $ $ $ $ ; $ $ ; % ; 2 ; ; ; % ; ; 2 2 2 2 3 2 5 6 2 5 * 6.bXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXoX, u p p p p u p p p p k k 9 z z k k k z k k k l l k l l l l J l l J J l l J g r.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXR. .......{ ....i.O.O.O...i.....i.O.O.y.O.#.#.#.u.y.u.u.u.u.u.u.p.u.u.p.p.p.p.p.6XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnX).z.z.x.x.x.z.x.x.x.V.V.x.V.V.V.V.V.V.C.C.A.A.C.A.A.S.S.S.J.S.MX", +"$ $ $ $ $ $ $ ; $ $ $ $ $ 2 ; 2 ; ; 2 ; ; 2 2 2 2 5 3 1 2 + 4.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXoX, p p u p u p p p p p p p k p k p k k p k k k l l k k l l l l l c l l c c c s r.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXR.[ ........@.....O.O.O.O.@.@.@.@.@.@.#.@.i.y.@.i.y.i.u.u.i.u.u.u.u.u.a.a.p.p.y.6XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXP.l.z.z.x.x.x.x.x.V.x.x.V.V.V.x.V.V.V.V.C.C.V.A.A.S.S.S.S.H.S.MX", +"$ $ $ $ $ $ $ $ 2 $ 2 2 $ $ 2 $ 2 2 ; 2 2 2 2 2 2 2 2 2 6 + 6.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXXX< u q u u p u p p p p p p p p p k p k k k k k k k k l l l k l l l l c l c c d r.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXxX;.[ ........O...@.O.O.O.@.@.O.@.@.@.@.@.#.@.@.i.@.i.@.u.i.u.u.u.u.u.u.u.p.p.p.p.8XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXP.l.z.z.z.x.x.x.z.V.x.V.V.x.V.V.V.V.V.V.V.V.A.C.A.C.V.S.A.H.S.MX", +"$ ; $ $ $ $ $ $ $ $ $ $ $ 2 ; ; ; ; 2 ; 2 2 2 ; 2 2 2 5 2 + 6.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXoX, q q p p u p u p u p p p p p k p k k k k k k k k k l k k l l l l l l l l c d 9.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXbXd.[ ........O...O.O.O.O.o.O.@.@.O.@.@.@.@.@.i.i.@.i.i.i.O.u.u.u.u.u.u.u.u.y.p.p.pXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXM.l.c.z.x.z.z.x.x.x.V.x.x.V.x.V.Z.V.V.V.V.C.C.V.C.A.A.S.C.J.S.MX", +"$ $ $ $ $ $ $ $ $ $ $ 2 $ $ ; ; ; ; ; ; ; % 2 % 2 2 2 2 2 + <.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX=X, y p q u u u u p p p p p p p p k p p k k p k k k k k k l k k l l l l l c l d 2.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlX&.[ { ............O.O...@.O.O.O.@.O.@.O.@.@.@.@.y.O.y.y.i.i.u.u.i.u.u.u.u.u.p.y.pXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXM.l.z.z.z.c.x.z.z.x.z.x.x.x.V.x.V.V.V.V.V.C.V.C.V.A.S.V.A.J.S.MX", +"$ $ $ $ $ $ $ $ $ $ $ $ $ ; ; ; ; ; ; % 2 ; 2 2 ; 2 ; 2 2 & >.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX;X9 q q p u u p u p p u u p p p p p k p p p k k k k k k k k l l k l l k l l l f ) vXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXuX} [ { ................@...O.O.O.@.@.@.O.@.@.@.@.@.@.i.i.y.y.i.i.y.i.i.y.s.u.u.p.fXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXM.l.l.z.z.z.z.x.x.z.x.x.x.x.x.V.x.V.V.V.V.V.V.V.C.A.C.A.A.J.A.MX", +"$ ; $ $ $ $ $ $ $ $ $ 2 ; $ ; ; ; ; ; ; ; ; 2 2 1 2 2 2 2 & >.bXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXwXq q q q u u u u u u p p p p p p p k p k k p p k k k k k k k k l l l l l l l l J gXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX6X} { ..{ { .......... ...O.O.O.O.O.O.@.O.@.@.@.@.y.@.@.@.i.u.O.i.i.i.i.y.u.u.u.a.xXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXcXc.l.z.z.l.z.z.z.z.x.x.x.x.x.x.V.V.x.x.V.V.V.V.V.V.A.V.C.A.J.S.MX", +"$ ; $ $ $ $ $ $ $ $ $ $ $ ; ; $ $ ; ; ; 2 ; ; ; ; ; 2 2 2 % C bXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXiXq q q u u u u p u p u u p p p p p p p p p k k p k p k k k k k l l k k l l l l f rXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX%X} { ..{ ..........O...O.O.O.O.O.@.O.O.@.@.O.@.@.@.@.i.@.@.i.y.i.y.y.i.y.i.u.y.s.xXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXzXc.l.z.l.z.l.z.z.z.x.x.z.x.z.x.x.x.x.V.V.x.V.V.V.V.V.A.A.V.Y.MXMX", +"$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; ; % ; ; ; ; ; 2 ; ; 2 ; 2 2 7 lXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXhXb q q q u q u q u u p p q p p p p p p p k p p k k k p k k k k k k k l l l l l s @XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXf.} { { { ......{ ..........O.O.O.O.O.@.O.O.@.@.O.@.@.@.i.@.O.u.i.y.i.i.i.i.p.y.d.bXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXaXz.p.l.z.z.z.l.z.z.z.z.z.Z.z.x.x.x.V.x.x.V.V.x.V.V.A.V.V.C.Y.MXMX", +"$ ; $ $ $ $ $ $ $ $ $ $ $ $ ; $ $ ; ; ; $ ; ; ; ; ; 2 2 2 % 7 hXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlXM , 4 q u u q p q p u p p q p p q p p p p p k k k p k k k k k k k k l l l k l s 9.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlX&.} { { { { { ..{ ............O.O.O.O.O.O.@.@.O.@.@.@.@.@.@.@.i.i.y.@.i.@.y.y.y.E.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX9Xl.l.l.l.z.z.l.z.z.l.z.c.x.z.x.x.x.V.x.x.V.x.V.V.V.V.V.V.A.H.MXMX", +"MX8 $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ % ; ; ; ; 2 ; ; 2 2 % 2 tXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX>.< q q q q u q q q q p p p p p p p p p p p p p p k p k k k k k k k k k l k l f E lXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX7X] ] ] { { { ..{ { ..{ ............O.O.O.O.O.O.@.@.@.@.@.@.y.@.@.@.@.i.i.u.i.u.y.^.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX6Xp.l.l.l.z.p.z.l.z.z.z.z.z.Z.x.x.x.x.x.V.x.V.x.x.x.V.V.V.A.S.MXMX", +"MX; $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ ; ; ; ; ; ; ; ; ; 2 % 2 :XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX6.* q 4 q q q q u u u u q p q q p p p p p k p p p p k k p k k k k k k l l l k h j ,XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX~.` { { { ] { { { ..{ { ..........O...O.O.O.O.O.@.O.@.@.O.@.@.@.@.@.i.@.y.u.i.i.@.%XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX2Xp.l.l.l.z.z.z.p.l.z.z.z.z.z.z.z.x.x.x.x.x.V.V.V.V.V.V.V.A.A.MXMX", +"MX8 = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ $ ; ; ; ; ; 2 $ 2 O +XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX X< 4 q q q q q q u u u p q p p p q p p p u k p p k p k k a k k k k k k k k l k r w.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnX-.` ] ] { { { { [ { { { ....{ ......O...O.O.O.O.O.O.O.@.O.@.@.@.@.@.i.@.@.@.u.y.y.6XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX'.p.l.l.l.p.z.z.z.l.l.z.z.z.z.z.c.c.x.x.x.x.x.x.V.x.V.V.V.A.V.MXMX", +"MX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; ; ; $ $ ; ; ; - ; O XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX;X6 4 4 4 q q q q u u u q p q p q p p p p p p p p p p p k p p k k k k k l k k l j E lXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX7X! ] ] ] { { { { { { { { { ..................O.O.O.@.O.@.O.@.@.@.@.@.@.@.u.@.@.@.i.aXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX_.p.p.p.z.p.z.z.z.z.l.z.z.z.z.z.z.z.z.x.x.x.x.x.x.x.V.x.V.A.V.MXMX", +"MX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; ; - ; ; ; % O 5.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXyX5 4 4 q 4 q q q q q q q q i q p q p q p p p p p k p k p k k k k k k k k k l k f s ;XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXR.! ] ] ] ] ] ] ] { { { { ..{ ............o.O...O.O.O.O.O.@.O.@.@.@.@.y.@.i.@.i.o.s.cXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXN.p.z.z.p.p.z.p.z.l.l.l.z.z.l.z.z.z.x.c.x.x.x.V.x.x.x.V.V.D.V.MXMX", +"MX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; % $ ; $ ; ; - O V mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlXb , 4 q 4 q q q q q q q p q p p q p q p u u p p p k p p k k p k k k k k k k k l j ,.nXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXuX] ' ] ] ] ] ] { ] { { ] { { { { { ..........O.O.O.O.O.O.O.@.O.@.@.O.@.@.@.@.@.u.o.E.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXxXb.p.p.z.z.p.z.p.z.l.l.z.z.l.c.z.z.z.z.x.x.x.x.x.x.V.x.x.V.S.x.MXMX", +"MX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ ; ; ; ; ; 7 lXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX>.> 4 4 4 q q q q q q q q q p q p q p u p p p p p p p p p p k p k p k k k k l k k s >XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX!.! ' ' ] ] ] ] { ] { ] { { { { { ..{ { ............O.O.O.O.O.@.O.@.@.@.O.@.@.@.y.o.^.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXdXp.p.p.p.p.z.p.z.p.l.l.c.p.l.z.l.z.z.z.z.z.x.x.x.x.x.x.x.V.S.V.MXMX", +"MX$ ; $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ = = = ; ; % ; tXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX X* 4 4 4 4 q q q q q q p q q p q p p q p p p p p p p p p p z k k k k k k k k l k j ,.nXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXpX| ' ' ' ' ] ] ] ] { { ] { { { { { { { { ............O.O.O.O.O.O.@.O.O.@.@.@.@.@.y.o.6XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX6Xp.p.j.l.j.l.j.j.l.l.l.z.l.z.z.l.z.z.z.z.z.x.z.x.x.x.x.x.x.H.x.MXMX", +"MX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ = ; = % = ; % -XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX:X6 4 4 4 4 4 4 q 4 q q q p q q q q i q p p q p q p p p a p p k p p k k p k k k k k s #XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX0.I ' ' ' ] ' ] ] ] ] ] { ] { { { { { { ....{ ............O.O.O.O.O.O.@.O.@.@.O.@.@.#.gXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX[.p.p.j.a.j.j.j.l.j.j.l.j.l.l.l.l.z.l.z.z.z.z.c.x.z.x.x.x.x.H.MXMXMX", +"MXMX: = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ = $ ; ; $ . XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXjXb > 4 4 4 4 4 q q 4 q q q q q p q q p q p p p p p p p p p p p p k k k p k k k k k j E jXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX1XQ ' ' ' ' ' ' ] ' ] ] { ] ] { ] { { { { { { { ..{ ........o.O.O.O.O.O.O.O.@.@.@.O.o.m.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXW.p.p.j.j.j.p.j.j.l.l.j.j.l.l.l.z.l.z.l.z.z.z.z.z.z.x.x.x.Z.A.MXMXMX", +"MXMX= $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; O Z mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX>.* 5 4 4 4 4 4 4 q q q q q q q p p q p q p p q p p p p p p p k p p k k p k k k k k r w.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXkX*.! ' ~ ' ' ' ] ' ] ' ] ] ] ] ] ] ] { { { { { { ................O...O.O.@.O.@.O.@.@. .^.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXn.p.p.p.p.j.j.j.j.l.j.l.l.j.l.l.l.l.l.z.x.l.z.z.x.x.z.x.x.x.x.MXMXMX", +"MXMX= = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ = $ $ $ $ ; $ % n vXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX.X+ 3 4 4 4 4 4 4 4 q q 4 q q q q i p q q p q p p p p p p p p p z p p k p k k k k k j t ;XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXf.Y ~ ' ' ' ' ' ' ' ' ] ' ] ] ] ] ] ] { { { { { { ..[ { ..........O.O.O.O.O.O.O.O.@.O. .6XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXfXp.u.p.p.j.p.j.j.j.j.j.l.l.j.l.l.l.l.l.l.z.c.z.z.z.z.c.x.z.V.x.MXMXMX", +"MXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ = $ $ ; $ $ tXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXrX3 6 3 5 4 4 4 4 4 q q q q q q q q q q p i p q p q p p p p p p p p p p k k p k p k k j v uXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX$XY Q ~ ~ ~ ' ~ ' ' ' ' ] ' ] ] ] ] ] ] { ] { { { { { ....{ ............O.O.O.O.@.@.O.o.s.cXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX3Xu.p.p.p.p.j.p.j.j.j.j.j.j.l.p.l.l.l.l.z.l.l.z.l.l.z.z.z.z.A.x.MXMXMX", +"MXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ = $ . OXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXM > 5 5 5 3 4 4 4 q q 4 q q q q q q q p q p q p q q p p p p p p p p p k p k p k k k k r W lXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX7XQ Y ~ ~ ' ' ~ ' ' ' ' ' ' ' ' ] ] ] ] ] ] ] { ] { { { { { { ............O.O.O.O.O.O.O. .R.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX/.y.p.u.p.p.p.p.p.j.j.j.j.j.j.l.l.l.l.l.l.z.z.l.z.l.z.z.z.x.D.x.MXMXMX", +"MXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; . 4.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX X@ 5 4 3 5 4 3 4 5 q 4 q q q q q q p q q q p q p p p p p p p p p k p p k p k k k k k k r 1.bXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXuX*.I ~ ~ ~ ~ ~ ~ ~ ~ ' ~ ' ' ' ] ' ] ] ] ] ] { ] ] { { { { { ..{ { ..........O...O.O.O.O. .3XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXN.y.p.p.p.p.p.a.j.p.p.j.j.j.j.p.l.j.l.j.l.l.l.l.z.z.z.z.z.x.A.MXMXMXMX", +"MXMXMX= $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X e vXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXwX3 3 3 4 5 5 4 5 4 4 4 4 4 q q q q q p q q q i p p q p p p p p p p p p k p k p k p k k k 0 2.nXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXuX*.I ~ Q U ~ ' ~ ~ ' ' ' ~ ' ~ ' ' ' ' ' ] ] ] ] ] { ] { { { { { { ..............O...O.O. .%.cXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXfXi.u.p.u.p.a.p.p.p.j.p.j.j.j.j.j.l.p.l.j.l.l.l.l.l.z.l.z.z.x.x.MXMXMXMX", +"MXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ wXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXC > 3 3 5 5 5 4 5 4 4 4 4 4 4 q q q q q q p p q q p p q p p p p p p p p p p k k k k p k f r 2.bXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXiX*.I Q Q ~ ~ U ~ ~ ~ ~ ~ ~ ' ' ' ' ' ' ' ] ' ] ] ] ] ] { { ] { { { { { { ..{ ..........O... .(.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX3Xy.p.y.u.p.p.p.u.p.p.p.p.j.p.l.j.p.l.l.l.l.l.l.l.l.l.z.l.z.x.z.MXMXMXMX", +"MXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX.X* 3 3 3 5 5 5 4 5 5 4 4 q 4 q q q q q q q q q i p q p q q p q p p p p p k k p k p k k k k r 1.kXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXyX*.D Q U Q U ~ ~ ~ ~ ~ ~ ~ ~ ~ ' ' ~ ' ' ' ' ' ' ] ] ] ] ] ] { ] { { { { { { { .............. .7XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX^.y.i.i.u.u.p.u.u.p.p.p.p.p.k.j.p.j.l.p.j.l.l.l.l.l.l.l.l.l.V.z.MXMXMXMX", +"MXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ V mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXhX4 1 3 5 3 5 5 3 4 4 4 q 7 4 q q q q q q q q p q q p q p p p p p p p p p p p p p k k k k k k 0 W rXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX$XT F U U Q Y ~ U ~ Q ~ ~ ~ ' ~ ' ~ ~ ' ' ' ' ' ' ' ] ] ] ] ] ] ] { ] { { { { { ..{ ........O.[ ;.xXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXxXs.y.u.i.u.p.u.p.u.p.a.p.p.p.p.p.l.p.p.l.j.k.j.l.l.l.l.l.l.z.A.c.MXMXMXMX", +"MXMXMX= = = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X 8 iXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX4.* 3 3 3 5 5 5 5 4 5 4 4 4 4 q 4 q q q q p q q p q p q p u p p p p p p k p k k k k p k k k k t z #XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXh.F G U U U U ~ U ~ ~ U ~ ~ ~ ~ ~ ~ ~ ' ~ ~ ' ' ' ' ' ' ' ] ' ] ] { ] { ] { { { { ..{ { { ......} %XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX7Xy.u.u.u.u.u.a.u.p.u.p.p.p.p.p.p.p.j.j.j.j.j.j.l.l.l.l.l.l.l.A.MXMXMXMXMX", +"MXMXMXMX= $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ .XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXeX1 1 3 3 3 3 5 5 4 5 4 4 5 4 4 q 4 q q q q q q p q p p q p u p p p q p p p p p p p k p k k k k j r 2.iXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX7X( D G U U U U U U Q U U ~ ~ U U ~ ~ ~ ~ ~ ~ ' ~ ' ' ' ' ' ' ' ] ] ] ] ] ] { ] { { { { { { { .... .&.fXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX^.o.u.u.#.u.u.u.p.u.p.u.p.p.p.p.p.j.p.j.j.j.l.j.j.j.l.l.l.l.c.z.MXMXMXMXMX", +"MXMXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ V mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXB + 3 3 3 3 3 5 3 5 4 5 4 5 4 4 q 4 q q q q q q p q q i u p u p p p u p p p p p p k k p k k k k k r v XXlXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXuXr.K D U G T T U U U U U U ~ U ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ' ~ ' ' ' ' ' ] ] ] ] ] ] ] { ] { { { { { { ....} (.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXn.y.y.i.u.u.u.u.u.u.u.u.p.j.k.u.u.p.p.j.j.j.j.l.j.l.j.l.l.l.x.l.MXMXMXMXMX", +"MXMXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X 8 iXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX-X& 2 3 3 3 3 3 5 5 5 5 5 4 4 4 q q q q q q q q q q p q u p q u p u p p p p p k p p p k p k k k k k j r W #XlXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXgXh.T A H L G U U U U U U U Q U ~ U U ~ U U ~ ~ ~ ~ ' ~ ' ~ ' ~ ' ' ' ' ' ' ] ] ] ] ] ] { ] { { { { { ..{ { aXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX7Xy.y.i.#.u.u.u.u.u.u.u.p.u.u.u.j.p.p.p.j.j.k.j.j.j.j.k.l.k.l.A.l.MXMXMXMXMX", +"MXMXMXMX$ = = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ .XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXC & 2 3 2 3 3 5 3 5 5 4 5 4 4 5 q q q q q q q q q q q p q p q q p p p p p p p p p p p k k p k p k k k j s E .XiXmXmXnXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnX1Xr.T A H K G G G G U G T U U U U U U U Y U U ~ ~ ~ ~ ~ ~ ~ ' ~ ' ~ ' ' ' ' ' ' ] ' ] ] ] ] ] ] { { ] { { { ] R.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX^.o.i.@.#.u.u.i.u.u.u.i.i.u.p.u.p.p.u.p.p.p.p.p.p.p.p.z.p.p.V.V.MXMXMXMXMXMX", +"MXMXMXMXMX; = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X N mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX+X+ 2 3 3 3 3 3 3 5 5 5 4 5 q q 5 q 5 4 q q q q q q p q p q p p p p q p p p p p a p z p k k k z k k k k k s d l 2.#XiXbXmXmXnXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlXyX@X) S R H K L L G G G G G U G U U G U U U U U Y U ~ U ~ U ' ~ ~ U ' ~ ' ' ~ ~ ' ' ' ' ] ' ] ] ] ] ] ] { { { { { { pXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXxXs.o.@.u.@.@.u.#.i.y.u.u.u.u.u.p.a.p.p.p.p.p.p.p.p.p.p.c.p.z.z.p.MXMXMXMXMXMX", +"MXMXMXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X X wXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXC + 3 2 3 3 3 3 3 5 3 5 5 5 5 5 5 q 5 q q q q q q q q q q q p q q p q p p p p p p p p p p k k k k k k k k k f s j l 1.w.#XrXuXcXbXmXmXmXmXnXnXjXgX,X@X9.( S g R K K K K K G L L G G G G G U G U U U U U U ~ U ~ U ~ U ~ U ' U ' ' ~ ~ ' ' ' ' ' ' ' ' ' ] ] ] ] { ] { { { ` R.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX3Xo.y.@.i.y.u.+.#.i.i.u.u.u.u.u.u.p.u.p.p.p.p.p.p.p.p.p.p.p.p.x.l.MXMXMXMXMXMX", +"MXMXMXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o 5.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX-X- 1 2 3 3 2 3 3 3 5 5 5 4 5 q 5 q q 4 4 q q q q q q q p q q p p p q p p p p p p k p k k p k p p k k k k l k l k f j s j k J W ) 1.9.2.q.1.) E J h d g h S J K S S K K K K L G L G K G G G G U G U U U U U U U ~ U ~ ~ ' U ' U U ~ ~ ~ ~ ' ~ ' ' ' ' ] ] ] ] ] ] ] ] ] ] { uXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXT.o.@.@.@.@.@.#.#.+.u.u.u.u.i.u.u.u.u.u.u.p.a.p.p.p.p.p.p.z.p.c.l.MXMXMXMXMXMX", +"MXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X 8 hXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXV + 2 2 2 1 3 3 3 3 5 5 5 5 5 5 q 5 q 4 q q q q q q q q p q p q p p q p p q p p p p p p p p k z p k k k k l k l k l l k l f f f d s d d f h h c S S J J J J J K K K K K K K K L L G K G G U G U G U U U U U U U ~ ' U U ' U ~ ' ~ ~ ~ ' ' ~ ' ~ ' ' ' ' ] ] ] ] ] ] { ` ~.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXuX@.o.y.@.@.u.i.@.@.u.@.u.u.u.u.u.u.u.u.u.p.u.p.p.p.a.p.p.p.p.z.c.MXMXMXMXMXMXMX", +"MXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ OXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXrX- - 2 2 2 3 3 3 5 3 5 5 5 5 5 5 5 q 4 4 q 5 q q q q q q q q p q q p p q p p p p p p p k k p z k p k k k k k k l k k l k l l l l l J c c c c c S l J J J J K J J K J K K K K L L G L L G G G G U G U U U U U U U U U ~ U ~ ~ U ' U ' ~ ~ ' ~ ' ' ' ' ' ' ' ] ] ] ] } &.kXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX(. .O.@.@.@.O.#.i.u.@.u.@.y.u.i.u.u.u.u.u.u.p.u.p.p.p.p.p.p.p.p.p.MXMXMXMXMXMXMX", +"MXMXMXMXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X N bXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX7.@ 2 2 3 2 3 3 3 3 5 3 5 5 5 5 5 5 4 4 q q 5 q q q q q q q q p p q p p q p p a p p p p k k k k p p k k k k k k l k l l k l l J l l h c c J c J c J J J J J J J J K K J K K L L L L G L G T G U U U T U U U ~ U U ~ U ~ ~ ~ ~ ~ ' ~ ' ~ U ' ~ ' ' ' ' ' ' ] ] ] ] ` &XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXfXs.O.@.O.@.#.@.@.@.@.u.@.@.u.@.u.u.u.u.u.a.u.u.u.p.p.p.p.p.p.p.z.p.MXMXMXMXMXMXMX", +"MXMXMXMXMXMXMX: $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X +XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXjXn % 2 2 2 2 2 3 3 3 3 3 3 q 5 5 q 4 5 4 4 q q 5 q q q p q q q q p q p p p q p p p p p p p p k k k p k k k k k l k k l l l k l l l l c c l c c J c J J J J J K J J K J K K K K L L L L G G G G G T U U U U U U ~ U U U U ~ U ~ U ~ ~ ~ ' ~ ~ ' ~ ' ' ' ' ' ' ] ! 0.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX%X .O.@.@.O.@.@.@.i.@.@.@.u.@.u.@.u.u.u.u.u.u.u.u.p.p.p.p.p.p.p.Z.MXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ V mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX+X+ 1 2 2 2 2 2 3 3 3 3 3 5 5 5 5 4 4 5 4 4 q q 4 q q q q q q p q p q p p q p q p p p p p p k k k k k k p k k k k k k l l l l l l h l l J l J J c J c J c J J J J K K J K K K K K L L L G G G G T U U U T U U U U ~ U ~ U ~ ~ ~ ~ ~ ~ ~ ' ~ ' ' ' ' ' ' ] ' ] ] yXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXxX-.o.O.O.O.#.O.O.#.@.i.@.@.i.@.y.i.@.i.y.i.s.u.u.u.p.u.u.p.p.k.l.k.MXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMX$ = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X :XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX4.O - 2 2 2 2 2 3 3 3 3 5 3 5 5 3 4 4 4 5 4 4 q q q q q q q q p q p q p p p p p p p p p p p p p p k k k k k k k k l l k k l l l l l l l c l l J J c J J J J J J J J K K J K K K L L L L G G G G G G G U U U U U ~ U U ~ U ~ ~ U ~ ~ ~ ~ ~ ~ ~ ' ' ~ ' ' ` ! *XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX%X .O.O.O.@.#.O.#.O.O.@.@.@.@.@.@.i.y.i.i.y.y.u.u.u.u.p.u.p.p.u.x.u.MXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMX= $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ V mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlX7 % 2 2 2 2 2 2 3 3 3 3 5 5 5 5 5 5 q 4 4 4 4 q q q q q q q q q q i p q p p p p p p p p p z k k p p k k k k k k k k k k l l k l l l c l J l c c J c c J J J J J K J K K K K K L L L L G G G G G G G U U U U U U U U U ~ U U ~ Q ~ ~ ~ ~ ' ~ ~ ~ ' ~ ' ! 0.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnX;. .O.O.O.O.O.@.O.#.@.@.y.@.@.i.@.y.@.i.i.i.i.i.y.u.u.u.p.u.u.k.l.MXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o :XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX-X% 1 2 2 2 2 2 3 3 3 5 3 3 5 5 5 5 5 4 4 4 4 q q q q q q p q p q q q p q q p p p p p p p p p p k k p p k k k k k l k l k l l l l l l l l c c c c c J l J J J J J J J K J K K K K L L L L G L G U U G G U U U U U U ~ U ~ U ~ ~ Q ~ ~ ~ ~ ~ ' ' ~ ' Y | uXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX%X ...O.O.O.O.O.O.@.O.O.@.O.@.@.@.i.@.i.y.u.O.y.y.s.i.u.u.u.u.u.l.u.MXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ V mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX|.O ; 2 2 2 2 3 2 3 3 3 3 5 5 5 4 5 4 4 4 4 4 q 4 q q q q q u q p p q p i u q p q p p p p p p k k k p k k p k k k k k l l l l l l l l l c c l J c J J J J J J J J J J J K K K K K K L L L G G G U G G U U U U U U U U ~ U ~ Q ~ ~ ~ ~ ~ ~ ~ ~ ' ! ! &XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXxX-. .....o.O.O.O.@.O.@.@.@.@.@.@.@.i.@.O.u.i.y.i.i.y.i.u.u.u.u.i.l.k.MXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMX= $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X X +XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXbXB O 2 2 2 2 2 3 2 3 3 3 3 3 3 5 5 5 4 3 q 7 4 4 q q q q q q q q q p q p p p p p p p p p p p p p p k p k p k k k k k k k l l l l l l c l c c l c l J l J l J J J J J K J K J K K K L L L L K G G G U T G U U U U U U U ~ U Q Q U ~ ~ ~ ~ ~ ~ ~ I !.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX%X} ....O...O.O.O.O.O.O.@.O.O.@.@.@.@.@.y.@.O.i.O.i.i.i.i.u.u.u.j.u.MXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o N bXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXiX7 O 2 2 2 2 2 2 2 3 5 3 3 3 3 5 5 5 4 4 q 4 4 4 q q q q q q p q q q p q q p p u p q p p p p p k k p k k k p k k k l k k l k k l l l l l l c l J l J J J l J J J K J K J K K K K K K L L L G G G G G G U U U U U ~ U U U Y Q Q Q ~ ~ ~ ~ ~ I =.nXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXfX%.[ ......O...O.O.O.O.O.O.O.@.@.@.@.@.@.@.@.i.y.u.O.u.i.u.u.u.u.c.i.MXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMX= : = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ OXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXeX2 % 2 2 2 2 2 2 2 3 3 3 5 3 5 5 3 3 5 4 5 4 4 4 q q q q q q u q u q p q p q p p p p p p p p p p k p k k k k k k k k l k l k l l l l l c l J l c l J l J J J J J J J J J K K K K L K L L G G G G G U T T U U U U U U U Q U Q Q Y ~ ~ ~ Y *.gXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX!.} { ........O.O.O.O.O.O.O.@.O.@.O.@.@.@.O.i.O.i.y.i.i.y.u.i.u.u.j.MXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X 8 hXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX+X+ % 2 2 2 2 2 2 2 3 3 3 3 5 5 5 5 5 5 5 q 5 q 5 q q q q q q q q p q p q p p p p p p p p p p p p k z p k k k k k k k k l l k l k l l l l l l l J J J l J J J J J J J J J K K K K K K L L L L G G G G G U G U T U U U U Y U U ' ' U Y ~ yXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXyX{ { ......{ ........@...O.@...@.@.@.@.@.O.@.@.@.@.#.@.#.#.#.u.u.k.u.MXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ 5.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX XO ; ; 2 2 2 2 3 2 3 3 3 3 5 3 5 5 5 5 5 5 5 q 5 q 4 q q q q q q q q p u q q p p q p p p p p k p p k p k p z k k k k k k l l l l l l l l J l l l l l J J l J J J J J K J K K K K K L L L L G G G G U G U G U U U U U U U ' U U Y Y >XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnX0.[ { ....{ ............o.O...@...O...@.O.@.@.@.@.#.#.#.#.#.#.#.#.j.u.MXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMX; = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X eXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX7.O % 3 2 2 2 2 2 2 3 3 3 3 5 5 3 q 5 5 q 4 5 q 5 q 4 q q q q q q i q q p q p p q p p p p p p p p p p k k k k k k k l k k k l l l l l l l l l J J J J J J J J J J J J J J J K K K K K L L G L G G G U T G U U U U U U U U Y U I $XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX&X} ] { { [ ....{ ......@.......@.@.O.@.O.O.@.@.O.@.@.@.@.@.#.#.#.u.i.MXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ N vXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXbX4.O % ; 2 2 2 2 2 2 3 3 3 3 3 5 3 5 5 5 5 4 4 q 4 q 4 q q q q p q q p q p q p p p u p p p p p p p k p k p k p k k k k l k k k l l l l l l J l l l l l J l J l J J J J J J K K K L K L L L G L G G G G G G U G U U U U U U I ~.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXkX&.} { { { ..{ { ............ .........@.O.@.@.@.O.#.O.@.@.#.@.#.#.j.#.MXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMX= = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ |.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnX4.O % 2 2 2 2 2 2 3 3 3 3 3 3 3 5 5 5 4 4 4 4 5 q 4 4 q q q q q p u q p u i p q q p p u p p p p k p k k k k k k k k k l l k l l l l l l l l l J l l J J J J l J J J J J K K K L L K K L L L L G G G G G U U U U U U U F ~.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXR.! { { { { { { { ................O.O.O.O.O.O.@.O.O.@.@.@.@.@.+.$.u.u.MXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X $ wXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX4.O % 2 2 2 2 3 2 2 3 2 3 3 3 5 3 5 3 5 5 4 4 4 4 q 4 q q q q q q p p q q p p p u p p p p p p p p p p k p k k k k k k k k k l k k l l l l l l l J l l J J J J J J J J J J K J S K K L L L L G G G G G G G U U U F F ~.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX>X` { ] { ] { { { ..{ { ..{ ......O.....O.O.O.O.O.@.@.@.O.@.O.@.+.$.k.#.MXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMX= $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o N bXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnX4.O % % 2 2 2 2 2 2 3 3 3 3 5 5 5 5 5 5 5 4 4 4 4 4 q q 4 q q q q q p q q q p u p p p p p p p p k p p p k k k k k k k l k k l l l l l l l l l l J l J l J J J J J J J J J J L K K L K L L L G G G G G G G U H F @XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXfX&.` ] ] ] { { { { { { { { ..{ ......O...O.O.O.O.@.O.O.O.@.@.@.@.@.+.i.MXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMX= = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ 7.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX7.O - 2 - 2 2 2 2 2 2 3 3 1 3 3 5 3 5 4 4 3 4 4 4 4 q q q q q q q q q p p q p u q p p p p p p p k p k k k p k k k k k k l k k l l l l l l l l J l l l J l J J J J J J J J J K K J K K L L L L G G G G G F F #XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX0.! ] ] ] { ] ] { ] { { { ..{ ..............O...O.O.O.O.@.O.@.O.@.@.k.O.MXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X X :XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX X# % 2 2 ; 2 2 2 2 2 2 3 5 3 5 5 5 5 5 4 5 4 4 q q 4 4 q q q q q q i q q q p u p p u p p p p p k k p p k k k p k k k l k l k l k l l l l l l l J l J l J J J J J J J J J L J K K L K L L L L G G G D T >XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX%X! ' ] ] ] ] ] ] { { { { { { { ..{ ..............o.O.O.O.O.O.@.O.O.$.#.MXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X 8 hXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX+X2 % ; 2 2 2 2 2 2 2 3 2 3 3 3 5 5 5 4 5 4 5 5 4 4 4 q q q q q p q q p p q q p q p u p p p p p p p k p k p k k z k k k k l l l l l l l J l l J l J J l J l J l J J J K J K J L J L K L L L G G A ( rXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXyX' ` ' ] ] ' ] ] { ] { ] { { { { { ..{ ............O...O.O.O.O.O.@.o.k.O.MXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMX8 $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ V bXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXeX7 % - 2 2 2 2 2 2 2 3 3 1 3 2 5 5 5 3 4 4 4 4 4 4 q q q q q q q q u p p q p p p p p p p p p p p p p p k p k k k k k k l k k l l l l l l l l l J l J l J J J J J J J J K J L J K K K K L G A 1.uXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXkX*.! ' ' ' ' ] ] ] ] ] ] { ] { { { { { { ..............O...O.O.O.O.@.@.$.MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ |.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXhX>.O - ; 2 2 2 2 3 2 3 5 5 2 5 3 5 5 4 5 4 5 q 4 4 q q q q q q q u q q u q p q u p u p p p p p k k p p k p k k k k k k l k k l k l l l l l l l l l J l J l J J J J J J J J L K K K K A D r.xXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX0.! ' ~ ' ' ] ' ' ] ] ] { ] { { { { { { { { ..{ ..........O...O.O.O.O.i.O.MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o OXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnX X; % 2 2 2 2 2 2 2 2 5 3 3 3 5 5 5 4 3 4 4 4 4 4 q 4 q q q q q q p u p q i p u p u p p p p p p k k p k p k k k k k k k l k l k l l l l l l J l J J l J J l J J J J J J J K K K D T $XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX~.Y ' ~ ' ' ' ' ' ] ' ] ] ] ] ] { ] { { { { { { { ............O.O.O.O.@.@.MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X $ eXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX:X7 O - 2 2 2 2 2 2 2 5 2 3 3 5 3 3 4 3 4 4 4 4 4 q 4 4 q q q q q q u q p u u u p u p p p p p p p p p k p k k k k k k k k k l k l l l l l l l l l J l J J J J J J J J J L S R 1.iXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX&XY ~ / ' ~ ' ' ' ' ' ' ] ] ] ] { ] { ] { { { { ..{ { { ........O...O.O.i.O.MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ 8 tXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXjX7.% + 1 2 2 2 2 3 2 3 3 3 3 3 5 5 4 3 5 4 4 4 4 q q q q q q q q p q p u p u p p u p u p k p p k p k k p k k k k k k l k k l l k l v l l J l J l l J l J J J J J J S A J @XbXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX7XQ Y ~ ~ ~ ' ' ' ' ' ' ] ' ' ] ] { ] ] ] { { { { { { ....{ ..........O.$.O.MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXX 8 $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ N jXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX:XN O % 2 2 2 2 2 2 3 3 3 3 3 5 5 5 5 4 4 7 4 q 4 q q q q q q q q q u u u u u p p p u p p p p p p k k p k p k k k k l k k l l l l l l l l l l J l J J l J J J h d 1.yXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXuX| I ~ ~ ' ' ~ ~ ~ ' ' ' ' ' ] ' ] ] ] ] { ] { { { { { { { ..{ { ..{ ..O.#.MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ N bXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlX X2 O - 2 2 2 3 3 3 3 3 3 3 5 5 5 4 5 4 4 4 4 q 4 q q q q q q u u u u u p u p p p p p p p k p p p k p k k k k k k k k l l k l l l l l l l J l c c J J l g E #XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXgX*.I Y ' ~ ~ ' ~ ~ ~ ' ' ' ' ' ] ' ] ' ] ] ] { ] ] { { { { { { ....O...{ $.O.MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ Z bXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXtX4.- O - 2 2 2 2 3 3 3 3 3 5 5 3 4 5 4 5 4 4 4 q 4 q q q q q q u u u u u u u u p u p p p p k p p k k p k k k k k k l k l k l l l l l l l c c c h f J g.kXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXkX:.I Q Q U ~ ~ ~ ' ~ ' ' ~ ' ' ' ' ' ' ] ] ] ] ] ] { ] { ] { { { { { { { O.O.MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ Z mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXeX>.- + - 2 2 2 3 3 3 3 3 3 4 3 4 3 4 4 5 4 4 4 4 q q q i q u u u u p u u u p p p p p p p k p k p k p p k k k k k k l l k l l l l l l l h d c 9.yXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXkX=.I U Q ~ ~ ~ ~ ' U ' U ' ' ~ ~ ' ' ' ' ' ] ' ] ] ] { ] { { { { { { { { X.$.{ MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X 5.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXeX>.- O 2 2 2 3 3 3 5 3 5 5 5 4 4 5 4 4 q 4 4 4 q q q q u u u u u u u u p u p u p p p p p p k p k k k k k k k k k k l l k l l l h r l 9.yXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXkXq.I U Y U ~ U ~ U U ~ ~ ' ' U ' ' ' ~ ' ' ' ' ' ] ] ] ] { ] ] { { { { { { O.O.MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ 5.mXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX:X4.3 @ - 1 3 3 3 3 5 5 5 3 5 4 5 4 4 4 4 4 q q q q q u q u u u u u p p u u p p p p p k p p j k a k k k k k k l k k l l j s P t.yXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXkX=.D U U U U ' ~ U ' ~ U ' ' ~ ~ ~ ~ ' ~ ' ' ' ' ' ] ' ' ] ] ] { ] { ] { { { $.MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X Z vXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXrX7.n + + 1 3 3 3 3 3 5 5 4 5 4 4 5 4 4 4 4 q q q q q u u u u u u u p p p p p p p p p p k p k p k k k k k k f j r j _ XXjXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXkXq.F U U Y U U U U ~ U ~ ' U ~ ~ ~ ' ' ~ ' ' ' ' ' ' ' ' ] ] ] ] ] { ] { { { $.] MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ Z vXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlX=X>.7 & + 1 2 3 3 4 3 4 4 4 4 4 4 4 4 q q q q q q q u u u u u p p u p p p p p k k p p k p k p k p j r s m w.,XnXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXgX:.D U U U U U U U U ~ ~ U U ~ ~ ~ ~ ' ~ ~ ~ ' ' ' ~ ' ' ] ' ] ] ] ] ] ] { ] ..{ MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X N hXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXiXOX>.7 > @ > 3 5 3 5 5 5 4 4 4 4 4 q q q q q u q u u u u u u p u u p p p u p p p p p t t 0 t m w.;XkXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXuX( A G G U U U U U U U ~ U ~ U ~ ~ U ~ ~ ~ ~ ' ~ ' ' ' ' ' ' ' ] ] ] ] ] ] ] | O.MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ N tXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXhX+X6.n 3 > @ - 6 3 4 3 4 4 4 4 4 4 q q q u u u u p u u p p u p p p p p y 9 0 t z ,.t.,XjXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX1XT D U G U G G U U U ~ U U Q U ~ ~ ~ ~ ~ ~ ~ ~ ~ ' ~ ' ~ ' ' ' ' ' ] ] ] ] ] ] O.] MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X 8 eXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXiXoX8.B b 4 > * * > < , 4 4 4 q 4 q q q q q q q 9 9 9 0 < , y p m 2.e.;XgXnXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX$XL A L G G G U G U U U U U Y U ~ U U U ~ ~ ~ ~ ~ ~ ~ ~ ~ ' ' ' ' ' ' ' ' ' ] ] X.{ MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X $ $ $ $ $ $ $ $ $ $ $ $ $ $ o $ .XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXlXrX=X X6.>.B b i 4 4 4 , 6 , 9 q q q q a M ,.3.e.XX;XtXlXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXg.D H G L G G U G U U U U Y T U U Q ~ U ~ ~ ~ U ~ ~ ~ ' ~ ' ~ ' / ' ' ' ' ' ' ] { { MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X o 5.vXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXlXjXiXwXrX,X;X=X;X,XwXrXiXjXlXnXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXjXq.R H K G G L G G G G U T T T U U U U U ~ U U ~ ~ U ~ ~ ~ ~ ~ ~ ' ~ ' ' ' ' ] ' ' X.] MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ N tXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXrX( g K L L L L L G G G G U T U U U U U U U ~ Q U ~ ~ ~ ~ ~ ~ ~ ' ~ ~ ' ' ~ ' ' ' ' X.' MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX; = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o 8 +XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX@XK R K K K K L L L L G G G G G G G U G U U U U U ~ U ~ Y U ~ ~ ~ ~ ~ ~ ~ ~ ' ~ ' ' X.' MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X o 5.lXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXgX9.g S J K K K K K L L L L G G G G G T U T U U U U U U Q U ^ Y Q ~ ~ ~ ~ ~ ~ ' ~ ~ ~ ] ] MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X N :XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX$XE g J J K J K J K K L L L L L G G G G G G U U U U U Q U U Q U Q Q Q ~ ~ ~ ' ~ ~ ' ' ' ] MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX$ = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ |.hXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXhX9.h h S J J J J K J K K K L K L L L L G L U G L U U G G U U U U Y U ~ Q U ~ ~ ~ ~ ~ ~ ~ { ~ MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ e +XmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmX#XP d J J J J J J J J K K J K K K K L L L L G G G U G G U U U U U U U U ~ U Q ~ ~ ~ ~ ~ ~ X./ MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o X Z tXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXyX1.d f J l J l J J J J K J J K K K K K L K L G L G G G G G T U T U U U U U U U Q Q ~ Q ~ Q | ~ MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X 8 |.jXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXjXw.h f l J l J J J J J J J J J J J J K K K K L L L L K G G G G U U U U U U U U U ~ Q Q Q Q Y ] ~ MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X e OXbXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlXXXP r h l l c c c J l J l J J J J J J J K K K K K K K L L L U G G L U T G U U U U Y U Q U Q Q ' ~ MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X N +XbXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlXoXE s f l l l l c c c l J J J J J J J J J J J K J K K K L L L L L G G G G U G G U U U U U U Q Q ~ / MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o o N +XvXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlXXXE r j l l l l l l c l c c c l J l l l J J J J J K K J K K K K L L L L G G G G U T U U T U U ^ U ~ Q MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ N .XhXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXhXXXE r j k k k l k l l l l l l c l l J J J J J J J J J J J K J K K K K K L L L G G G G T G U U U U U ~ ~ MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ 8 |.tXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXyXw.m 0 t k k k k k l k l l k l l l l J l l c c c J J J J J J K K J K K K K K K L L L G G G G G U U U U Q Q MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X 8 Z +XjXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXjX#X1.p 0 j k z k k k k l k l l l l l l l l l J J l J l c J l J J J J J K J K J K K K L L L L G G G G L U L ^ Q MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = $ = = $ $ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X X e |.wXvXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlX,Xw.m 9 0 p k k p k k a k k k k l l k k l l l l l l l J l J c J J l J J J J J J J K K K K K K L L K G G G G G Q Q MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = $ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X X N |.eXvXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXlX:Xe.W 9 0 q p k p p p k k k k k k k k k k k l l l l l l l l l c l c c c J J J J J J J J J J K K K K K L L L G G K ^ U MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ 7 5.+XhXbXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXiX#X6.M 9 0 9 p u p p p p p k p p k k k k k k k k l k l l l l l l c l J c c c c J J J J J J J J J K K K K J K K L L L L ^ U MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o o X 8 V |.+XhXbXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXvXiX+Xe.,.u , , 9 p u u p p p p p p k p k p k p k k k k k k k l k k k l l l l l l l c c J l l l J J J J J J J J K K K K K K K L ^ U MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ X o X 8 N 4. X+XtXlXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXmXnXlXtX=X X6.M i , < , 9 u u u u p p u u p p p p p p p k p k p k k k k k k l k l k l k l l l l l c l c J l J J l J c J J J J J J K K K K L ^ L MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX$ 8 $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ = = $ X X o $ 8 7 V <.|.OX-XeXtXjXjXvXvXbXmXmXmXmXnXvXvXvXjXhXtX,X=X.Xe.4.,.b 4 6 < * , 4 q q q u q u u u u u p u p p u p p p p k p p k p p k k k k k k k l l k l l l h l l c c l J l J J J J J J J J K K J J K K ^ L MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ # X . . X % ; 2 7 7 C 7 N V V >.V B n M n 7 q 5 5 > & * * > 1 , 4 4 4 4 4 q q q q q q q q q l q p p p u p p p p p k p k k k p p k k k k k k l k k l l l l l l c c c c c J c c J J J J J J J J K ^ K MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= : = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ % $ ; % % ; $ % % % % % + + O + + - & - - 1 1 3 1 5 5 5 5 4 4 4 4 4 4 4 q 4 q q q q q q q q q q u u u p p p p p p p p p p p k k k k k k k k k l l k l l l l l l c c c c c c J c c c J J J P L L K MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; ; $ % ; ; ; % 2 ; ; ; 2 2 ; 2 2 2 3 3 3 2 5 2 5 5 5 5 5 4 5 4 4 4 q 4 q q q q q q q q z p u u p u p u p p p p k p k k p p k k k k k k k l l l l l k l l l c c c c c c c J c J J J L P MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ $ ; $ ; $ 2 2 ; 2 ; 2 ; 2 2 2 2 2 2 2 3 5 2 5 5 5 5 5 5 5 4 4 4 4 4 q q q q q q q q q q u u u u u p p p p p p p k p p k k p k k k k k l k k k l l l l l l l c c c c c c x J c J T K MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ ; ; ; ; ; ; ; ; 2 ; 2 ; 2 2 3 2 2 3 2 5 2 6 5 5 2 5 5 5 4 4 4 5 4 4 4 q q q q q l q q u u u p p u p u u p p p p p k p p k p k k k k k l k l f k l l l l c l c c c c c c x P L J MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ ; ; ; $ ; - ; 2 ; 2 2 ; ; 2 2 2 3 2 5 5 2 5 5 5 5 5 5 5 4 4 4 4 4 q q q q q q q q u u u u u p u p p p p p p a p p k p k k k k k k k k k k l k l l l l l l l c c c c P P c MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= : = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ ; ; ; ; ; ; ; ; - 2 2 2 2 2 2 2 2 2 2 1 5 5 5 5 5 5 5 5 4 4 4 q q q q q q q q u u u p u u u p u u p p p p k p p p k p k p k p k k k l k l k l c k l c l c l c L J MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ ; $ ; ; ; ; ; 2 ; 2 ; ; 2 2 2 2 5 2 5 2 2 5 5 5 5 5 4 5 4 4 q q q q q q q q 4 u u u u u u p p p p u p p u k p k p k p k k k k k k k k k l k k f l l c l c P c MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX$ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ = = ; ; ; ; ; ; ; 2 ; 2 ; 2 2 2 2 2 2 2 2 5 2 5 5 6 5 5 q 5 5 4 q q 5 q q 5 q q u u q u u u u u u p p p p p p p p p k p p k k k k k k k k k l l k c l l c c c MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= 8 = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ $ ; % ; ; ; ; ; % 2 2 ; 2 2 2 2 2 5 5 2 5 2 5 5 5 5 5 5 q 5 q q q q q q 4 u u q u u u u u u u u p u p p p p p p p k p k p k k k k l k k k k l v c l MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ $ ; ; $ ; ; ; ; ; - ; 2 ; ; 2 2 2 2 2 2 2 2 2 5 2 5 5 5 5 5 5 q 5 5 5 q q q q q q u q u u u u p u p u p p p p p p k k p k z k k k k k k l k c k c c MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; ; ; ; ; ; ; 2 2 ; 2 ; 2 2 2 2 2 2 2 5 2 5 2 5 5 5 5 5 5 q 5 q q 5 q q 4 q q u q u u q u u p u u u p p p p p p p k p p p k p a k k k c v l MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = ; $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ $ ; ; ; ; ; - ; 2 2 2 2 2 2 2 2 2 2 5 2 5 6 5 5 5 5 5 5 5 5 5 q q q q q u q q u q u p u u u p u p p p p p p p p p k p p k k k l v v MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ $ ; ; ; ; ; ; 2 % 2 ; 2 2 2 2 2 2 2 1 2 2 5 5 5 5 5 5 q 5 q 5 5 4 4 q 4 q q u u q u q u u p u p q p p p p p p p p k k a k v l MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX$ = 8 $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ = ; ; ; ; % ; ; ; ; 2 ; 2 ; 2 2 2 2 2 2 5 5 2 5 1 5 5 5 5 5 5 q 5 4 q 4 q q q q q u q u u u u u p p p p p p p p p p k k v k l MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX$ = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ = $ $ $ ; $ ; ; ; ; ; ; 2 ; 2 2 2 2 2 2 2 2 2 5 2 5 5 5 5 5 5 5 5 q 4 4 4 4 q q q q u u q u u u u u q p q p p p p p p z a k MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; ; % ; ; ; ; ; ; ; 2 2 2 2 2 2 5 2 5 5 5 2 5 5 5 5 5 5 4 4 4 5 4 w q q q u q u u u u u u p p u i t a v z z MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = : = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ ; % $ ; ; ; ; 2 ; ; 2 ; ; 2 2 2 2 2 2 2 2 2 5 5 5 5 5 5 5 4 4 4 4 4 4 4 q q q 4 u q u u u u u u u a a a z MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = = = = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ $ ; ; ; ; ; % 2 ; 2 2 2 2 2 ; 2 2 2 2 2 5 2 5 5 5 5 5 5 5 4 4 4 4 q 4 q q i q q q u u u u u p a p p MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ ; $ ; ; ; 2 ; 1 ; ; 2 2 2 2 2 2 2 5 2 2 1 5 5 5 5 3 4 4 4 4 4 q 4 4 4 u q w u w a a a a MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ ; $ ; ; ; $ 2 ; ; ; ; ; 2 2 2 2 2 2 2 2 5 2 5 5 5 2 5 5 5 5 4 4 4 4 4 q 4 4 q i u a p w a MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = : = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ $ ; % $ % ; ; ; 2 2 2 ; 2 2 2 2 2 2 2 2 2 5 5 5 3 4 5 5 4 4 5 4 4 4 u i i u u MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX$ $ = : = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; ; $ ; ; ; ; ; ; % ; ; 2 % 2 ; 2 2 2 2 5 5 2 2 5 3 5 5 5 3 4 4 4 i i u 4 w MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ ; % ; ; ; 2 ; ; ; 2 2 2 2 2 2 2 2 5 6 2 3 5 5 3 5 w i i 4 4 MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX$ $ = = = = = $ $ $ = = $ $ $ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ = = = = % ; ; ; ; ; ; ; 2 ; ; 2 2 2 2 2 2 2 2 3 3 5 w w w 5 5 MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= = = : = = = = = $ $ $ = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ = = ; = ; ; ; ; ; 2 ; 2 ; 2 2 2 1 1 2 2 7 q w 3 3 3 MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX= $ = = = $ = = = $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ = $ $ ; ; $ ; ; ; ; ; ; : ; 2 2 2 7 5 3 2 3 MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX$ = = = ; $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ; $ ; = ; : 3 2 3 % 2 2 2 MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX$ $ $ $ ; 8 ; ; = = = = = = = = $ = = = = $ $ = = = = = $ $ $ $ = = = = = ; ; : 2 2 : = = ; 2 MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX$ $ $ $ $ $ ; = = = : = = = = = = = = = = ; 8 ; ; ; = = $ $ $ $ $ $ = MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX$ = = $ $ $ = $ $ $ $ ; = MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX", +"MXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMXMX" }; diff --git a/share/pixmaps/bitcoin32.png b/share/pixmaps/bitcoin32.png index 367abfcc..3cf61983 100644 Binary files a/share/pixmaps/bitcoin32.png and b/share/pixmaps/bitcoin32.png differ diff --git a/share/pixmaps/bitcoin32.xpm b/share/pixmaps/bitcoin32.xpm index bffedd4c..2573bc6a 100644 --- a/share/pixmaps/bitcoin32.xpm +++ b/share/pixmaps/bitcoin32.xpm @@ -1,140 +1,219 @@ /* XPM */ -static char *bitcoin__[] = { +static char *bitcoin32[] = { /* columns rows colors chars-per-pixel */ -"32 32 102 2", -" c #CC7D1D", -". c #D5831F", -"X c #D48221", -"o c #D98621", -"O c #DC8820", -"+ c #DC8D2C", -"@ c #D98F36", -"# c #D68F39", -"$ c #DD943E", -"% c #E28B23", -"& c #E98F24", -"* c #E18F2D", -"= c #ED9124", -"- c #EC942A", -"; c #F59624", -": c #F89724", -"> c #F79827", -", c #F89825", -"< c #F0962B", -"1 c #F59A2D", -"2 c #F99B2B", -"3 c #EC9732", -"4 c #EC9A37", -"5 c #E2963B", -"6 c #E6983A", -"7 c #EC9C3B", -"8 c #F69D33", -"9 c #F99E32", -"0 c #F49E3A", -"q c #F9A036", -"w c #F6A13C", -"e c #F9A33B", -"r c #D79341", -"t c #DC9641", -"y c #E39A43", -"u c #EA9D42", -"i c #EFA041", -"p c #EDA34B", -"a c #F5A443", -"s c #F9A643", -"d c #FAA846", -"f c #F2A64C", -"g c #F9AA4B", -"h c #E5A251", -"j c #ECA756", -"k c #EBA758", -"l c #FAAF57", -"z c #FBB057", -"x c #FBB25B", -"c c #DFB179", -"v c #E4AA65", -"b c #EBAE64", -"n c #E9AF69", -"m c #FBB665", -"M c #F1B46A", -"N c #F8B96D", -"B c #E5B071", -"V c #EBB777", -"C c #EEB877", -"Z c #E7B478", -"A c #EBB97D", -"S c #F0B671", -"D c #F2B871", -"F c #EFBC80", -"G c #E6BD8D", -"H c #EDBF88", -"J c #E6BF90", -"K c #F1C187", -"L c #F1C288", -"P c #E5C093", -"I c #EEC493", -"U c #E1C19B", -"Y c #E9C69C", -"T c #ECC89D", -"R c #F1C897", -"E c #DFC5A4", -"W c #DBCBB8", -"Q c #E2C7A7", -"! c #EBCBA6", -"~ c #E6CBAB", -"^ c #E9D2B7", -"/ c #E5D1B9", -"( c #EBD6BD", -") c #EFD9BE", -"_ c #DDD0C2", -"` c #DCD7D2", -"' c #DEDEDE", -"] c #ECDAC5", -"[ c #EDDECB", -"{ c #E9E0D5", -"} c #E7E0D9", -"| c #E9E2DB", -" . c #EFE8DF", -".. c #E5E5E5", -"X. c #EBE7E2", -"o. c #EFEAE6", -"O. c #ECECEC", -"+. c #F2ECE6", -"@. c #F1F0EE", -"#. c #F4F4F4", -"$. c #FBFBFB", -"%. c None", +"32 32 181 2 ", +" c #093662", +". c #0D3A65", +"X c #0C3F68", +"o c #133F69", +"O c #103C67", +"+ c #0C446B", +"@ c #0B486E", +"# c #024168", +"$ c #1B456E", +"% c #0A4E71", +"& c #034E71", +"* c #1C4771", +"= c #1C4A75", +"- c #174D76", +"; c #045575", +": c #0D5475", +"> c #045C79", +", c #195276", +"< c #165C7C", +"1 c #195C7E", +"2 c #165478", +"3 c #244C74", +"4 c #03647E", +"5 c #15607E", +"6 c #375C80", +"7 c #036C83", +"8 c #096A83", +"9 c #076680", +"0 c #166581", +"q c #1A6285", +"w c #146B85", +"e c #176D8A", +"r c #02778A", +"t c #037C8D", +"y c #0D798C", +"u c #087287", +"i c #13738A", +"p c #127A8E", +"a c #137D91", +"s c #3D6385", +"d c #336F8B", +"f c #387992", +"g c #416486", +"h c #477692", +"j c #4C7693", +"k c #537C97", +"l c #00818F", +"z c #028291", +"x c #018A95", +"c c #0C8E9A", +"v c #0B8694", +"b c #118293", +"n c #108997", +"m c #13869A", +"M c #118D9A", +"N c #198797", +"B c #02939B", +"V c #0D949E", +"C c #08989F", +"Z c #00999E", +"A c #10919C", +"S c #1C919D", +"D c #37879A", +"F c #0F97A2", +"G c #039BA0", +"H c #0B9BA2", +"J c #1193A2", +"K c #3A9AA6", +"L c #00A3A4", +"P c #0CA2A6", +"I c #0DA5AA", +"U c #01ABA9", +"Y c #0AACAC", +"T c #14A7AA", +"R c #01B3AE", +"E c #09B1AF", +"W c #0CAFB1", +"Q c #05B6B1", +"! c #09B5B2", +"~ c #03BCB4", +"^ c #08BBB5", +"/ c #0ABCBA", +"( c #1AB2B1", +") c #32A9AF", +"_ c #3AAAB0", +"` c #3DB9BA", +"' c #56859D", +"] c #558FA3", +"[ c #449AA8", +"{ c #5E96A9", +"} c #6497AA", +"| c #69A7B5", +" . c #60B2BA", +".. c #00C1B6", +"X. c #05C5BA", +"o. c #0AC3BF", +"O. c #02CCBD", +"+. c #00D0BF", +"@. c #2EC2BE", +"#. c #38C0BE", +"$. c #6AB9C0", +"%. c #06CCC0", +"&. c #08CCC5", +"*. c #03D4C3", +"=. c #00DCC7", +"-. c #01DCC9", +";. c #36CAC4", +":. c #26D8CB", +">. c #3DDED1", +",. c #01E4CD", +"<. c #0DE4CF", +"1. c #00EBCF", +"2. c #03E4D1", +"3. c #00ECD3", +"4. c #0FEDD5", +"5. c #12EED5", +"6. c #00F1D7", +"7. c #00F5DA", +"8. c #2CE2D1", +"9. c #2DEFDA", +"0. c #22ECD7", +"q. c #3DE6D6", +"w. c #38ECDA", +"e. c #35F0DC", +"r. c #00FEE2", +"t. c #5DC1C4", +"y. c #49DAD0", +"u. c #55DBD2", +"i. c #59DBD4", +"p. c #66C7C8", +"a. c #7DDEDA", +"s. c #48E7D8", +"d. c #55E1D6", +"f. c #65EADE", +"g. c #71E7DD", +"h. c #7FEBE2", +"j. c #7DF6E8", +"k. c #8CAABB", +"l. c #86A9BA", +"z. c #93AABC", +"x. c #9DAFC1", +"c. c #86B6C2", +"v. c #91BAC6", +"b. c #A5B6C6", +"n. c #8ACACF", +"m. c #9CC4CE", +"M. c #83CFD1", +"N. c #9BC8D0", +"B. c #89D7D7", +"V. c #9CD9DB", +"C. c #A4C9D2", +"Z. c #A7D6DA", +"A. c #BADADF", +"S. c #90E1DE", +"D. c #8AEBE3", +"F. c #98E5E1", +"G. c #A0E2E1", +"H. c #BCEBEA", +"J. c #BAE9E8", +"K. c #A2F8EF", +"L. c #C4D2DB", +"P. c #C9D7DF", +"I. c #C6DFE4", +"U. c #C9DFE4", +"Y. c #D8DFE6", +"T. c #C3E1E4", +"R. c #C3ECEB", +"E. c #CAEFEE", +"W. c #D4E6EA", +"Q. c #DCEDEF", +"!. c #C3FAF4", +"~. c #D6F2F1", +"^. c #DBF5F4", +"/. c #DCFBF8", +"(. c #E0E6EB", +"). c #E2EBEF", +"_. c #E4F4F4", +"`. c #EDFBFA", +"'. c #FFFFFF", +"]. c #F3F7F8", +"[. c None", /* pixels */ -"%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.%.%.%.%.%.%.t 5 5 $ %.%.%.%.%.%.%.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.%.%.%.r u w q 9 9 9 8 4 # %.%.%.%.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.%.y s e 9 2 , , , : > 2 9 q 5 %.%.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.s q 2 , , , , : , > 2 2 > > 2 9 %.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.t e 1 , , , , : : ; > 2 9 9 2 , , > 2 + %.%.%.%.%.%.", -"%.%.%.%.%.$ e 2 , , , , , , ; u u 8 1 1 2 > , , > > + %.%.%.%.%.", -"%.%.%.%.%.e 2 , , : > ; ; > < ` ` 0 c n 1 2 , , , > , %.%.%.%.%.", -"%.%.%.%.e 1 , , , , ; h v - 3 ..! w ' _ 9 2 > , , , > : %.%.%.%.", -"%.%.%.6 q , : , > 2 > W ..| [ #.H V ..D 9 9 2 , , , , , % %.%.%.", -"%.%.%.e 2 , > 2 2 2 9 b ! #.$.$.#.#.#.Y i 1 2 > , , , > ; %.%.%.", -"%.%.@ q > 2 2 2 9 q e q 0 o.$.+.) { #.#.| b 2 2 , , , , : X %.%.", -"%.%.4 9 2 2 9 q e e s w b O.#.( m x I @.$...f > > , , , : & %.%.", -"%.%.8 > 2 2 9 e s d g a P #.#.L x l a [ $.#.A 2 2 , : , , ; %.%.", -"%.+ 1 , , 2 2 q e d g f / $.#.T n k Z o.$.O.M 9 2 > , , , ; X %.", -"%.* 2 , , , 2 9 q e s f X.$.#.O.O.O.#.$.+.Y g e 9 2 , , , ; o %.", -"%.* 2 , , , 2 2 q e w n O.$.[ R ( O.$.$.[ d s e 9 2 2 , , ; o %.", -"%.+ 2 , , , > 2 8 8 1 G #.#.T m m N ] #.#.~ s e e 9 2 > : ; X %.", -"%.%.> , , , , 2 < v B [ $.O.m z z s b #.$...g e e q 9 2 ; = %.%.", -"%.%.= : , , , : 7 ' O.#.$.@.C j p u ~ #.$.} g q 9 9 2 2 ; % %.%.", -"%.%.o , , , , : 0 G ^ .$.#.O.X.{ X.#.$.#.Y e 9 2 2 > , ; %.%.", -"%.%.%., : , , , 2 2 2 M O.) ] #.#.#.#.O./ d 9 2 > , , ; = %.%.%.", -"%.%.%.& ; , , , , 2 ; Q ..g F O.K A H S s 9 2 > , : , ; o %.%.%.", -"%.%.%.%.; ; , , , , 2 E _ d ' ..d q q 9 2 > , : , , ; = %.%.%.%.", -"%.%.%.%.%.; : , , , 2 q d g U J e 2 2 > , , , , , ; = %.%.%.%.%.", -"%.%.%.%.%.o ; : , , , 2 9 q 9 q 9 > , : , , , , ; = . %.%.%.%.%.", -"%.%.%.%.%.%.. ; ; , , > 2 2 2 > , , , , , , , ; = %.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.= ; : > 2 2 , , : , , , , ; ; & %.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.%.. = ; > : , , , , ; ; = = X %.%.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.%.%.%. % = ; ; ; ; & O %.%.%.%.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.%.%.%.%.%.%. X X %.%.%.%.%.%.%.%.%.%.%.%.%.%.", -"%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%." +"[.[.[.[.[.[.[.[.[.[.[.[.2.3.3.3.6.6.6.[.[.[.[.[.[.[.[.[.[.[.[.[.", +"[.[.[.[.[.[.[.[.[.*.-.4.3.3.2.3.6.7.r.r.7.7.[.[.[.[.[.[.[.[.[.[.", +"[.[.[.[.[.[.[./ &.&.O.+.*.=.-.,.3.3.3.3.6.7.r.7.[.[.[.[.[.[.[.[.", +"[.[.[.[.[.[.! o.^ X...X.&.8.q.s.w.0.3.3.3.3.3.r.7.[.[.[.[.[.[.[.", +"[.[.[.[.[.W W Y E R ;.F.^.'.'.'.'.'.!.5.3.3.3.3.7.r.[.[.[.[.[.[.", +"[.[.[.[.I I P L ( S.'.'.'.'.'.'.'.'.'.9.3.1.1.1.3.7.r.[.[.[.[.[.", +"[.[.[.A F V B ` _.'.'.'.H.g.u.y.u.h.d.,.1.e.K.j.3.3.7.7.[.[.[.[.", +"[.[.a J M x _ ].'.'.J.#.R R ..X.O.*.-.,.<.`.'.'.j.1.3.r.3.[.[.[.", +"[.[.m b v S _.'.'.M.L L E ^ ^ X.o.O.*.*.8.'.'.'.K.1.3.3.r.[.[.[.", +"[.e a p r Z.'.'.n.x P I Y E Q ^ X.O.O.+.h.'.'./.9.3.3.3.r.1.[.[.", +"[.e i 8 D '.'.J.x V H P I L U ! ~ ~ ..u.`.g.8.<.1.1.3.3.6.6.[.[.", +"[.e w 4 m.'.'.K l A V C Z p.H.~.R.S.H.'.y.O.*.=.,.w.4.3.3.r.[.[.", +"< q 5 w Q.'.I.r b n A V Z.'.'.'.'.'.'.J...O.*.*.f.'.!.<.3.r.3.[.", +", 1 : f '.'.c.7 p b l $.'.'.'.'.'.'.'.S.~ X.O.O.D.'.'.5.3.7.3.[.", +"* 2 % ' '.'.{ 7 p y l T.'.'.'.'.'.'.'.J.Q ^ X.X.i.'.'.q.,.6.3.[.", +"$ * + ' '.'.] 4 i i p Q.'.'.'.'.'.'.'.~.R ! X...y.'.'.s.=.,.3.[.", +"$ $ . k '.'.{ > w i u T.'.'.'.'.'.'.'.J.U E Q ~ u.'.'.>.*.,.,.[.", +"$ * o g '.'.l.; 0 w 4 | '.'.'.'.'.'.'.t.L Y ! R B.'.'.:.+.,.,.[.", +"$ = $ $ W.'.P.: 5 0 w 8 C.'.'.'.'.'.V.B P P Y R E.'.^.X.%.2.*.[.", +"[.= $ z.'.'.h % 5 5 w 8 | A.W.A. .x V H P L ` '.'.S.~ X.2.[.[.", +"[.* * O g '.'.L.@ 2 5 0 w 4 8 y r t c A V H G E.'.'.@.~ o.O.[.[.", +"[.$ = $ b.'.'.z.# 2 5 0 w i i p b b M c x n.'.'.V.U ! &.^ [.[.", +"[.[.= * o 3 (.'.'.k.@ % < 0 w w i p y t z n.'.'.^.T I E &.[.[.[.", +"[.[.$ = $ . g ].'.'.L.h % & : > 4 4 u [ T.'.'.`.) Z I ! Y [.[.[.", +"[.[.[.* = $ g (.'.'.'.L.k.} ] } c.U.'.'.'.Q.K x H I Y [.[.[.[.", +"[.[.[.[.= * $ . 3 b.'.'.'.'.'.'.'.'.'.'.'.N.N z A A I [.[.[.[.[.", +"[.[.[.[.[.= * $ o s z.Y.].'.'.'.'.W.v.D 7 y b M J [.[.[.[.[.[.", +"[.[.[.[.[.[.= = $ $ O $ 6 j k h d < > 4 i i m m [.[.[.[.[.[.[.", +"[.[.[.[.[.[.[.* = 3 * * $ o O + @ : 2 5 0 i a a [.[.[.[.[.[.[.[.", +"[.[.[.[.[.[.[.[.[.* * = = * $ $ = , 1 q q w [.[.[.[.[.[.[.[.[.[.", +"[.[.[.[.[.[.[.[.[.[.[.[.$ $ $ $ $ = , [.[.[.[.[.[.[.[.[.[.[.[.[.", +"[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[." }; diff --git a/share/pixmaps/bitcoin64.png b/share/pixmaps/bitcoin64.png index 08c676ae..9767689d 100644 Binary files a/share/pixmaps/bitcoin64.png and b/share/pixmaps/bitcoin64.png differ diff --git a/share/pixmaps/bitcoin64.xpm b/share/pixmaps/bitcoin64.xpm index 851829d4..b917cc0a 100644 --- a/share/pixmaps/bitcoin64.xpm +++ b/share/pixmaps/bitcoin64.xpm @@ -1,242 +1,293 @@ /* XPM */ -static char *bitcoin__[] = { +static char *bitcoin64[] = { /* columns rows colors chars-per-pixel */ -"64 64 172 2", -" c #8F6319", -". c #8F6A1A", -"X c #90651A", -"o c #916C1A", -"O c #AF7C1E", -"+ c #B1781E", -"@ c #9A7026", -"# c #AC801F", -"$ c #B1811F", -"% c #A9812B", -"& c #B08320", -"* c #BB8621", -"= c #BD8E22", -"- c #A58132", -"; c #FC8400", -": c #FD8A03", -"> c #FD8E0C", -", c #FF910E", -"< c #F98F14", -"1 c #F79117", -"2 c #FD9314", -"3 c #FC951B", -"4 c #FE9A1D", -"5 c #CA8E22", -"6 c #CC8E2A", -"7 c #D48D23", -"8 c #C39223", -"9 c #CE9925", -"0 c #D19C25", -"q c #D19329", -"w c #D5992B", -"e c #DD9D33", -"r c #D69F3C", -"t c #E29425", -"y c #E79925", -"u c #EA9926", -"i c #E69A2C", -"p c #F79625", -"a c #F99524", -"s c #F79825", -"d c #F89825", -"f c #F3962A", -"g c #F69B2C", -"h c #F89B2B", -"j c #E19F30", -"k c #EE9B34", -"l c #F49D33", -"z c #F99E32", -"x c #F39F3B", -"c c #DFA731", -"v c #D7A43D", -"b c #DCA63C", -"n c #EEA328", -"m c #FFA225", -"M c #FFAB26", -"N c #F3A529", -"B c #FEA429", -"V c #F4AB2A", -"C c #FFAC2A", -"Z c #FFB325", -"A c #FFB42C", -"S c #FFBB2D", -"D c #E3A335", -"F c #E5A438", -"G c #EDA03D", -"H c #F7A037", -"J c #FAA135", -"K c #F3AB31", -"L c #FEAB31", -"P c #F4A13C", -"I c #F9A33B", -"U c #FDB432", -"Y c #FFBF37", -"T c #FFC12F", -"R c #FFC230", -"E c #FFC03E", -"W c #DFAF41", -"Q c #ECA34D", -"! c #EDA84E", -"~ c #F2A343", -"^ c #FAA642", -"/ c #FAA846", -"( c #F1A74C", -") c #F6A94F", -"_ c #FAAA4A", -"` c #E7A451", -"' c #ECA754", -"] c #EFAA56", -"[ c #ECAC5B", -"{ c #F3AA52", -"} c #FCAE52", -"| c #FBB056", -" . c #FBB25C", -".. c #E7AB61", -"X. c #ECB067", -"o. c #E7B36D", -"O. c #EBB36C", -"+. c #F2B163", -"@. c #FCB460", -"#. c #F0B56B", -"$. c #E3B274", -"%. c #EDB672", -"&. c #EDB877", -"*. c #E2B57C", -"=. c #ECB97B", -"-. c #E4BA83", -";. c #EBBD83", -":. c #E7BF8D", -">. c #EBBD88", -",. c #E9C08C", -"<. c #E7C496", -"1. c #EBC393", -"2. c #EBC997", -"3. c #E7C49A", -"4. c #E9C69A", -"5. c #E3CA9D", -"6. c #E9C89E", -"7. c #DCC9AE", -"8. c #DDCBB2", -"9. c #E3C7A2", -"0. c #E5CAA3", -"q. c #E9CBA3", -"w. c #E5CEAB", -"e. c #E8CEAA", -"r. c #E4D4AC", -"t. c #EBD2AF", -"y. c #E7CFB2", -"u. c #E1D4B4", -"i. c #E8D5B6", -"p. c #E5D7BB", -"a. c #E9D6BB", -"s. c #E5D8B9", -"d. c #EAD8BE", -"f. c #F0D6B4", -"g. c #DFDFC6", -"h. c #E3D6C1", -"j. c #E9D7C0", -"k. c #E6DAC5", -"l. c #EBDCC7", -"z. c #E5DCCA", -"x. c #EADEC9", -"c. c #E8DFD0", -"v. c #D7E2D9", -"b. c #E3E0C9", -"n. c #EEE2CB", -"m. c #E6E1D4", -"M. c #E9E2D3", -"N. c #E4E4DC", -"B. c #E9E5DE", -"V. c #F4EDDE", -"C. c #DFE8E6", -"Z. c #DEEEE8", -"A. c #DFF2F3", -"S. c #DDFFFF", -"D. c #E1E6E0", -"F. c #E8E6E2", -"G. c #E8E9E5", -"H. c #E5EFEC", -"J. c #E8E9EA", -"K. c #EAF3EE", -"L. c #F3F3EB", -"P. c #E7EDF2", -"I. c #E8EEF3", -"U. c #E7F4F7", -"Y. c #E9F0F7", -"T. c #EBF5FD", -"R. c #E4FEFF", -"E. c #ECFCFF", -"W. c #F4F5F4", -"Q. c #F4FFFF", -"!. c #FEFFFF", -"~. c None", +"64 64 223 2 ", +" c #073561", +". c #093663", +"X c #0D3A65", +"o c #093F68", +"O c #143F69", +"+ c #05436A", +"@ c #09426A", +"# c #054A6E", +"$ c #09496E", +"% c #14416A", +"& c #1B456E", +"* c #0C4D71", +"= c #054E71", +"- c #1B4671", +"; c #1B4B74", +": c #1D4C78", +"> c #0B5375", +", c #0C5C7B", +"< c #055A78", +"1 c #155476", +"2 c #195276", +"3 c #195679", +"4 c #165D7C", +"5 c #18597A", +"6 c #31587D", +"7 c #2C5279", +"8 c #0D627F", +"9 c #03627D", +"0 c #16617F", +"q c #195E81", +"w c #046B83", +"e c #0B6B84", +"r c #006780", +"t c #166581", +"y c #196285", +"u c #156C85", +"i c #186D89", +"p c #027286", +"a c #0C7087", +"s c #047589", +"d c #027C8D", +"f c #0A7D8E", +"g c #0E7289", +"h c #13748A", +"j c #127A8E", +"k c #1E768D", +"l c #127E91", +"z c #167792", +"x c #296685", +"c c #3C6284", +"v c #3D6C8B", +"b c #336685", +"n c #25758D", +"m c #367992", +"M c #48698A", +"N c #467B95", +"B c #577895", +"V c #4C718F", +"C c #00818F", +"Z c #018491", +"A c #0C8393", +"S c #028A95", +"D c #0E8997", +"F c #0F8E9A", +"G c #018F98", +"H c #118394", +"J c #108996", +"K c #148399", +"L c #118C99", +"P c #00949B", +"I c #0E939D", +"U c #019A9F", +"Y c #10919C", +"T c #2C8296", +"R c #2C8D9D", +"E c #388C9E", +"W c #22919E", +"Q c #019DA0", +"! c #0D9BA2", +"~ c #0E96A1", +"^ c #1194A4", +"/ c #109DA9", +"( c #2F94A2", +") c #209BA4", +"_ c #3B94A3", +"` c #02A4A5", +"' c #0CA2A6", +"] c #0CA6A9", +"[ c #01ACAA", +"{ c #0AACAC", +"} c #00B3AE", +"| c #0AB1AF", +" . c #0EABB1", +".. c #02B6B1", +"X. c #09B5B2", +"o. c #05BDB5", +"O. c #08BBB5", +"+. c #09BEBA", +"@. c #1AACAE", +"#. c #2BA6AC", +"$. c #39A5AD", +"%. c #28B8B7", +"&. c #32BCBB", +"*. c #3AB8B9", +"=. c #36AFB3", +"-. c #65839E", +";. c #448EA1", +":. c #5A8AA1", +">. c #4A9DAB", +",. c #6B8EA6", +"<. c #678BA4", +"1. c #6896AA", +"2. c #789DB1", +"3. c #7391A9", +"4. c #54A2AF", +"5. c #58B4BB", +"6. c #49AEB5", +"7. c #61AAB5", +"8. c #6EABB8", +"9. c #77A7B7", +"0. c #68B3BC", +"q. c #7EB1BE", +"w. c #01C1B6", +"e. c #06C4BA", +"r. c #09C1BB", +"t. c #04CABE", +"y. c #16C6BD", +"u. c #00D0BF", +"i. c #2BC1BD", +"p. c #05CEC1", +"a. c #09CAC4", +"s. c #03D3C3", +"d. c #02D9C6", +"f. c #06D5C9", +"g. c #03DDC9", +"h. c #0AD2C5", +"j. c #17D5C7", +"k. c #11CDC1", +"l. c #25CCC3", +"z. c #37C8C3", +"x. c #25D6C9", +"c. c #3DDDD1", +"v. c #01E3CD", +"b. c #00E9CF", +"n. c #02E6D2", +"m. c #00EDD3", +"M. c #0EECD4", +"N. c #06EBD6", +"B. c #19EBD5", +"V. c #17EED7", +"C. c #00F1D6", +"Z. c #00F4DA", +"A. c #00F8DD", +"S. c #25E5D3", +"D. c #37E4D5", +"F. c #27F0DA", +"G. c #00FEE4", +"H. c #4AC9C6", +"J. c #59C7C7", +"K. c #5BD7D1", +"L. c #65C3C5", +"P. c #76C6CA", +"I. c #68DBD5", +"U. c #6FD5D3", +"Y. c #49E6D8", +"T. c #56E8DB", +"R. c #63E8DD", +"E. c #79E0DA", +"W. c #5FF3E3", +"Q. c #6DEDE1", +"!. c #63EFE1", +"~. c #65F4E4", +"^. c #96A9BC", +"/. c #97B1C1", +"(. c #9DAFC1", +"). c #A8B8C8", +"_. c #8CC3CB", +"`. c #89D8D7", +"'. c #9BDEDD", +"]. c #99C7CF", +"[. c #A8C3CE", +"{. c #B3C1CF", +"}. c #B6CBD5", +"|. c #A2D2D7", +" X c #B2D4DA", +".X c #BCD4DB", +"XX c #93F5EB", +"oX c #9AF5EB", +"OX c #80F0E5", +"+X c #A7EAE6", +"@X c #B9E6E7", +"#X c #BCF2EE", +"$X c #ACF9F0", +"%X c #B3F7F0", +"&X c #C2D5DD", +"*X c #C3CED9", +"=X c #C8DDE2", +"-X c #D5DDE5", +";X c #CCECED", +":X c #C7E6E8", +">X c #D3E1E7", +",X c #D7E7EB", +" a H h h d d d d d d d d s a R ~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.U a s d d d d d d d d d d d d h h h h z : e.!.!.p.2 3 8.D.5.' a h h h d d d d d d d d p d A ~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.U M p d d d d d d d d d d h h 1 : : 2 h h p B.!.Q.%., l J.!.R.-.> z h h h d d d d d d d d p C N ~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.S a d d d d d d d d d d h d 3 7.r.O.G p ; k E.!.T.( , [ E.!.T.~ 4 z h h h d d d d d d d d d a S ~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.V d s d d d d d d d d h h h 2 l E.!.Q.T.m.:.q.!.!.l.: : -.Q.!.c.a z z z g h h d d d d d d d d s m A ~.~.~.~.~.~.~.", -"~.~.~.~.~.~.@ S a d d d d d d d h h h h z : *.R.!.!.!.!.Q.!.!.!.V.,.Q d.!.Q.1.2 I z z h h h d d d d d d d d d a S X ~.~.~.~.~.~.", -"~.~.~.~.~.~.U d s d d d d d h h h h h g z a [ 5.M.Q.!.!.!.!.!.!.!.Q.E.!.!.Q.&.; 3 J H z h h h d d d d d d d d s h C ~.~.~.~.~.~.", -"~.~.~.~.~.~.S a d d d d h h h h h h z z z I d > < %.W.!.!.!.!.!.!.!.!.!.!.!.W.s.[ > 4 H g h h d d d d d d d d d a S ~.~.~.~.~.~.", -"~.~.~.~.~.i M p d d d h h h h g z z z z J H I I J > x.!.!.!.!.Q.T.E.Q.!.!.!.!.!.E.u.f 2 H h h h d d d d d d d d p C 7 ~.~.~.~.~.", -"~.~.~.~.~.C a d h h h h h g g z z z J J I I I I J P J.!.!.!.!.d.P =.e.G.E.!.!.!.!.Q.Z.f 2 z h h d d d d d d d d d d A ~.~.~.~.~.", -"~.~.~.~.~.A a h h h h h g z z z J H I I I I ^ / d X.E.!.!.!.Q.1.4 I J I ;.U.!.!.!.!.!.N.1 h g h h d d d d d d d d a S ~.~.~.~.~.", -"~.~.~.~.6 C p d h h h z z J J J I I I I ^ ^ ^ _ a 3.Q.!.!.!.E.#.I . ._ 3 ] K.!.!.!.!.E.O., z h h h d d d d d d d p A + ~.~.~.~.", -"~.~.~.~.i B d d h h h g z J I I I I ^ ^ ^ / / _ h k.!.!.!.!.J.) } . .| .3 6.Q.!.!.!.Q.q.> z g h h d d d d d d d d B t ~.~.~.~.", -"~.~.~.~.B d d d d h h h z z J I I ^ / / / _ _ ^ ( I.!.!.!.Q.d.I . . .| .d 1.Q.!.!.!.Q.q.2 z h h h d d d d d d d d d B ~.~.~.~.", -"~.~.~.~.C a d d d d h h g z J H I ^ ^ / _ _ } J %.E.!.!.!.Q.;.4 _ } | } J f m.!.!.!.!.Q.;.2 J z g h h d d d d d d d a A ~.~.~.~.", -"~.~.~.~.C a d d d d h h h z z J I I ^ ^ / _ } z 6.Q.!.!.!.!.n.<.&.+.{ ) ] h.Q.!.!.!.!.R.~ d H z z h h h d d d d d d a A ~.~.~.~.", -"~.~.~.~.A a d d d d d h h g z z H I I ^ / _ _ z k.!.!.!.!.!.!.Q.E.I.F.F.T.Q.!.!.!.!.E.9.2 I J z z h h h d d d d d d d A ~.~.~.~.", -"~.~.~.~.S a d d d d d h h h z z J I I ^ ^ / I ( P.!.!.!.!.Q.Q.!.!.!.!.!.!.!.!.!.!.E.w.d J I I J z h h h d d d d d d d A ~.~.~.~.", -"~.~.~.~.A a d d d d d d h h h z J J I I ^ / h O.E.!.!.!.Q.f.1.z.Y.E.!.!.!.!.!.!.L.! , ^ / I I H z z h h h d d d d d d A ~.~.~.~.", -"~.~.~.~.S p d d d d d d h h h z z J I I ^ / d <.Q.!.!.!.E.+.d _ +.>.k.E.!.!.!.!.Q.s.P J _ ^ I I J z z h h h d d d d d A ~.~.~.~.", -"~.~.~.~.C a d d d d d d d h h g z z H I I ^ d k.!.!.!.!.J.{ | @.} I I O.H.!.!.!.!.Q.C.l I ^ I I H J z g h h d d d d a A ~.~.~.~.", -"~.~.~.~.B a d d d d d d d h h h h z z J I J x P.!.!.!.Q.j.I . . . . .B { K.!.!.!.!.Q.0.a / ^ I I J z z h h h d d d a A ~.~.~.~.", -"~.~.~.~.B d d d d d d d d d h h h J h f 2 ; [ E.!.!.!.Q.1.I . . .| | .d 4.Q.!.!.!.!.m.z I ^ I I I J z h h h h d d d B ~.~.~.~.", -"~.~.~.~.u B d d d d d d d d h h z , ' v.q.X.M.!.!.!.!.E.#.^ . .| } } } d >.Q.!.!.!.!.F.x J I I I J J z z h h h d d C t ~.~.~.~.", -"~.~.~.~.7 C p d d d d d d d d h h : y.Q.Q.Q.!.!.!.!.!.B.d B / _ } } } J 1 k.!.!.!.!.!.c.s J I H J J z z z h h h h s A + ~.~.~.~.", -"~.~.~.~.~.A a d d d d d d d d h > ` R.!.!.!.!.!.!.!.!.L.q.=.[ ~ z h h l 0.Q.!.!.!.!.Q.q.2 I J J z z h h h h h h h a S ~.~.~.~.~.", -"~.~.~.~.~.C d d d d d d d d d d > ..g.Y.E.Q.!.!.!.!.!.!.Q.E.T.B.k.a.d.P.Q.!.!.!.!.!.E.[ 2 J z z z g h h h h d d d d C ~.~.~.~.~.", -"~.~.~.~.~.y C p d d d d d d d d g 3 > l [ <.x.W.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.!.Q.z.> z z z h h h h h d d d d p C 7 ~.~.~.~.~.", -"~.~.~.~.~.~.S a d d d d d d d d d h h 3 , > ; =.Q.!.W.T.Q.!.!.!.!.!.!.!.!.!.!.!.Q.A.g 2 z h h h h h h d d d d d a S ~.~.~.~.~.~.", -"~.~.~.~.~.~.C h s d d d d d d d d d h g z H : <.!.!.t.l &.V.!.!.Q.Q.Q.Q.!.Q.Q.E.b.l > H h h h h h d d d d d d s m C ~.~.~.~.~.~.", -"~.~.~.~.~.~.X S a d d d d d d d d d h h h h p N.!.Q.=.: < c.!.Q.2.&.e.a.d.i.6.[ < 2 z h h h h d d d d d d d d a S ~.~.~.~.~.~.", -"~.~.~.~.~.~.~.A h s d d d d d d d d d h g 2 ~ E.!.E.{ 2 [ E.!.T.l : 2 1 3 2 > > h z h h h h d d d d d d d d s m A ~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.S a d d d d d d d d d h h : -.R.!.B.h 2 =.Q.!.M.p z z z h h z g h h h d d d d d d d d d d d a S ~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.N C p d d d d d d d d d h 3 ' 2.N.9.2 3 z.!.!.q.> J z h h h h h h d d d d d d d d d d d d p C n ~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.S h p d d d d d d d d d z 3 : p l J g 8.T.S.O.> z h h h h h d d d d d d d d d d d d d p h S ~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.S a s d d d d d d d d h h z d h I J a P o.P d g h h h d d d d d d d d d d d d d d s a S ~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.* S a s d d d d d d d d h h g z J J h 3 > d z h h h d d d d d d d d d d d d d d s a S * ~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.$ T a s d d d d d d d h h h z z z h g g h h d d d d d d d d d d d d d d d d s a T O ~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.& S a p d d d d d d h h h z g h h h h h d d d d d d d d d d d d d d d d p a S # ~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.8 S d p d d d d d d h h g h h h h d d d d d d d d d d d d d d d d d p h S = ~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.S A a s d d d d h h h h h d d d d d d d d d d d d d d d d d s a A S ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.0 T m p d d d d h h h d d d d d d d d d d d d d d d d d p B S 9 ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.V S m a p d h d d d d d d d d d d d d d d d d p a m S V ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.o V S C d p p d d d d d d d d d d d d p p d C S N . ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.5 C S A B d d a a d d a a a d B A S C 5 ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.O t B A A A A A A A A B t O ~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.", -"~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~." +"aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXn.m.m.C.m.m.m.C.C.m.aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXg.g.n.G.Z.Z.C.C.m.m.C.C.Z.G.G.G.Z.C.C.aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXh.s.n.n.v.g.g.v.v.b.b.b.N.m.m.m.m.m.m.m.Z.G.G.C.Z.aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXp.h.g.s.h.h.s.f.g.g.g.v.b.b.N.m.m.m.m.m.m.m.m.m.m.Z.G.Z.Z.aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXr.e.h.e.e.t.p.u.s.s.g.d.g.v.v.v.b.b.m.C.m.m.m.m.m.m.m.C.m.m.G.Z.Z.aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaXaXaXaXaX+.a.r.r.e.e.t.t.t.s.h.s.d.g.g.g.v.v.b.m.b.m.C.m.m.m.m.m.m.m.m.m.C.G.Z.aXaXaXaXaXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaXaXaXX.r.+.X.O.w.O.r.r.t.t.t.s.s.s.d.d.d.d.v.v.b.b.C.m.m.m.m.m.m.m.m.m.m.C.b.A.G.C.aXaXaXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaXaX .+.| | X.X.O.O.r.e.r.e.t.e.t.s.x.Y.T.R.Q.Q.T.Y.F.m.m.m.m.m.m.m.C.m.m.b.C.m.m.G.Z.aXaXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaX{ X.] { .X.X.X.O.O.w.o.o.l.R.+X5XiXiXiXiXiXiXiXiXiX6XXXM.m.m.m.m.m.m.m.m.b.m.m.m.G.Z.aXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaX] .' ] { { { | X.X.} o.H.+XuXiXiXiXiXiXiXiXiXiXiXiXiXiXiX$Xm.m.m.m.C.m.m.m.C.m.m.m.m.G.Z.aXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaX! ] ! ] ] ] ] { { } ` H.#XiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXuXM.m.m.m.m.m.m.m.m.m.m.m.m.m.G.Z.aXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXI / ! ! ! ' ] ] ] ` | '.iXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXwXN.m.m.m.m.m.C.b.m.m.m.m.m.m.m.G.m.aXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXF / I I I ! ! ' ' U *.4XiXiXiXiXiXiXiXiXiXiXwX2X#X%X#X4XeXiXiXeXY.v.b.m.m.b.m.V.F.N.m.m.m.m.m.m.m.G.m.aXaXaXaXaXaXaX", +"aXaXaXaXaXaX^ F I I I I I ' P J.iXiXiXiXiXiXiXiXyX@XI.z.y.e.t.t.t.s.h.D.Y.S.g.v.b.b.m.V.$XiXiX6X~.m.m.m.m.m.m.C.G.aXaXaXaXaXaXaX", +"aXaXaXaXaXJ L J Y I I I I G L.iXiXiXiXiXiXiXeX`.i.....o.w.e.t.t.p.s.s.s.s.g.g.v.v.v.v.7XiXiXiXiXiXW.b.C.m.m.m.m.Z.C.aXaXaXaXaXaX", +"aXaXaXaXj Y H J J J F Y G 5.iXiXiXiXiXiXiX'.@.[ } ..O.O.r.e.e.e.t.p.t.s.s.d.g.g.v.v.Q.iXiXiXiXiXiX6XN.m.m.m.m.m.m.G.m.aXaXaXaXaX", +"aXaXaXaXH H H H D J L Z $.iXiXiXiXiXiXeXJ.` ` | | | O.O.O.r.e.e.e.t.u.h.s.s.d.d.g.g.XXiXiXiXiXiXiXiXV.b.m.m.m.C.m.C.C.aXaXaXaXaX", +"aXaXaXh K j j H H H A Y .iXiXiXiXiXeX#.G I ! ' ' ] ] { | | | O.O.O.e.r.r.r.t.t.t.s.t.T.iXiXiXiXiXiX#XM.m.m.m.m.m.m.m.m.m.G.m.aXaXaX", +"aXaXu h i h h j j s :XiXiXiXiXiX5.S I I ! ' ' ] ] ] { | | X.X.o.O.r.e.e.e.t.t.t.j.wXiXiX4XoXE.T.b.b.m.m.m.m.m.m.m.m.m.Z.m.aXaXaX", +"aXaXi u u i h h w >.iXiXiXiXiX|.Z I I I I ! ! ! ' ] ] { [ [ [ [ } ..O.w.e.w.w.x.2XiX2XY.d.d.v.v.b.b.m.b.m.m.m.m.m.m.m.m.G.aXaXaX", +"aXt i 0 u h h h w .XiXiXiXiXuXW Z J F I I ! ! ! ' ' ` ` ] &.J.U.J.i.O.O.o.l.E.eXiX#Xt.u.d.d.g.g.v.v.b.v.m.m.m.m.C.m.m.m.G.m.aXaX", +"aX4 u 0 0 0 h 8 T uXiXiXiXiX_.s J L I I I I I ! ! U ' P.4XiXiXiXiXiXwX2X4XiXiXiXwXy.t.h.s.h.d.g.g.v.v.M.B.b.m.m.m.m.m.m.Z.m.aXaX", +"aXy 4 0 0 0 u 9 9.iXiXiXiXuXR f J J J J F I I I P #.;XiXiXiXiXiXiXiXiXiXiXiXiXiXI.w.t.s.h.s.s.d.f.d.R.wXuX$XM.b.m.m.m.m.C.C.aXaX", +"aXy 4 4 0 0 0 9 XiXiXiXiX.Xp H H H J J F I Y G #.eXiXiXiXiXiXiXiXiXiXiXiXiXiXuXy.e.e.t.t.u.s.s.d.S.iXiXiXiXXXb.m.m.m.m.m.G.aXaX", +"2 q 5 4 0 0 0 0 9XiXiXiXiX8.p j H H H J J J F F ,XiXiXiXiXiXiXiXiXiXiXiXiXiXiX3Xw.w.e.e.t.h.t.h.u.R.iXiXiXiX6Xb.m.m.m.m.m.G.aXaX", +"2 5 2 5 5 0 9 m iXiXiXiXiXE p j h j H H J J C P.iXiXiXiXiXiXiXiXiXiXiXiXiXiXiX8X..w.e.e.e.t.h.u.t.D.iXiXiXiXuXB.b.m.m.m.m.G.m.aX", +"; 3 2 1 3 4 > :.iXiXiXiXqXk a h j j H H H A D 1.iXiXiXiX>Xe u h j j j j H f _ iXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXi...O.r.r.r.e.t.t.s.7XiXiXiXiXT.g.v.v.m.m.Z.m.aX", +"- ; ; 2 2 2 # 2.iXiXiXiX&X8 u h h h h H j d 7.iXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXJ.} O.O.r.w.e.t.t.t.3XiXiXiXiXR.d.v.v.n.m.C.m.aX", +"& - ; 2 2 2 # 2.iXiXiXiX.X< u u i h h j j p 0.iXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXJ.} | O.O.O.w.e.e.e.8XiXiXiXiXR.d.v.v.v.b.m.m.aX", +"& & & 2 ; 2 $ 2.iXiXiXiX&X, t u u u h h h s 7.iXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXH.` | X.X.w.r.r.w.e.2XiXiXiXiXT.u.d.g.v.v.m.b.aX", +"& & & & - ; @ ,.iXiXiXiX>X8 0 t u u h i h a E iXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiX%.[ | | X.X.O.w.w.r.5XiXiXiXiXT.u.d.g.g.v.N.n.aX", +"& ; & & & & @ B iXiXiXiXqXy 0 t 0 u u u h g h 0 0 u u u h h w 8.iXiXiXiXiXiXiXiXiXiXiXiXiXiXiXL.Q { ] { | | X.O...i.iXiXiXiXuXj.s.s.s.d.g.C.g.aX", +"aX: & & & & % - ,XiXiXiXiX2.# 4 0 0 0 u u u u g >XiXiXiXiXiXiXiXiXiXiXiXiXiX@XQ ' ] ] { | | | | } E.iXiXiXiX2Xt.t.s.h.s.s.C.aXaX", +"aX: & & & & & X {.iXiXiXiX.X# 4 0 0 0 u u u i w T pXiXiXiXiXiXiXiXiXiXiXiX 5 0 0 0 0 u u u w T =XiXiXiXiXiXiXiXiXiX:X) P ~ ' ' ' ' ] { { [ i.iXiXiXiXiXK.w.t.t.p.p.s.g.aXaX", +"aX& - & & & & O 7 uXiXiXiXiX^.+ 5 5 0 0 y t u u u w a 8.=XiXiXiXiXiX:X0.S P Y Y I I ' ' ' ] ] ` '.iXiXiXiXeXy.o.r.t.t.p.f.h.aXaX", +"aX& : & & & & & . {.iXiXiXiXtXx * 5 4 4 0 0 0 0 u u u 9 g E 4.7.>.( A C D F Y I I I ' ' ] ] Q %.uXiXiXiXiX+X} o.o.r.e.t.v.p.aXaX", +"aXaX; - & & & & X V iXiXiXiXiX{.+ 2 3 5 5 0 0 0 t u u h h a p p s s A H J J F I I I I I ! ' U @XiXiXiXiXiXz.} O.o.e.e.r.f.aXaXaX", +"aXaX- & & & & & & X *XiXiXiXiXiX,.+ 2 5 5 0 0 0 t u u u h h h h j j H H H J J F I I I ' ~ P L.iXiXiXiXiX@X[ O.O.O.O.r.p.r.aXaXaX", +"aXaX& : & & & & & X M iXiXiXiXiXyXv + 5 3 0 0 0 0 0 t u u h h h j j j H H J J J F Y Y I G =.iXiXiXiXiXiX&.[ | O.O.O.O.f.r.aXaXaX", +"aXaXaX- & & & & & & (.iXiXiXiXiX9X6 + 3 3 5 0 0 0 t u u u u h h j j H j H H J J F Y Z #.qXiXiXiXiXiX`.` { | | X.X.o.r.aXaXaXaX", +"aXaXaX& ; & & & & & O & >XiXiXiXiXiXpXM + 2 3 3 4 4 0 0 t u u h i h h j j H H H J F C $.qXiXiXiXiXiX;X' ] ] { | X.X.p.O.aXaXaXaX", +"aXaXaXaX- & & & & & - X M tXiXiXiXiXiXtX,.# $ 5 5 4 4 0 0 u t u h h h h h H H H f C 0.iXiXiXiXiXiXuX#.` ] ] { { { X.O.aXaXaXaXaX", +"aXaXaXaX& : & & & & & & B iXiXiXiXiXiXiX{.6 + * 5 4 4 0 0 u t u u h h h j p p R XiXiXiXiXiXiXiX6.P ! ' ] { { { r.| aXaXaXaXaX", +"aXaXaXaXaX- - & & & & & & . -.iXiXiXiXiXiXiXyX/.v $ # * 5 4 0 t t t e e r w E ].uXiXiXiXiXiXiXiX5.G I ' ' ' ' ] X.{ aXaXaXaXaXaX", +"aXaXaXaXaXaX: & & & & & & & B tXiXiXiXiXiXiXiXiX.X2.N x 4 , , 8 u n ;.q.=XiXiXiXiXiXiXiXiXqX6.Z I I I ! ! ' ] X.aXaXaXaXaXaXaX", +"aXaXaXaXaXaX& : & & & & & & & . c -XiXiXiXiXiXiXiXiXiXiXqX,X=X&X=X,XtXiXiXiXiXiXiXiXiXiXiX:X( Z F I I ! ! ! / X.] aXaXaXaXaXaXaX", +"aXaXaXaXaXaXaX& : & & & & & & & . & ^.iXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXuX/.H f J D I I I I ! .! aXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaX& : & & & & & & & O . M }.iXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiX X_ p f H H A J F I I ] ! aXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaX& : & & & & & & & & X X M ).tXiXiXiXiXiXiXiXiXiXiXiXiXiXiXiXqX[.E w w j l H H J J L L / ^ aXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaX- : & & & & & & & & & X . 7 -.).-XyXiXiXiXiXiXiXiXyX>X[.1.n 9 w h h j j l l H J J ^ L aXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaXaX- : & & & & & & & & & & O X . & 6 V -.,.3.1.<.N x , < < 0 u u u h h h j l j H ^ J aXaXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaXaXaX- ; - - & & & & & & & & & & % % X @ @ @ @ $ > 1 4 0 0 0 t u u u h h h l K K H aXaXaXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaXaXaXaXaX- : & & & & & & & & & & & & & & - ; ; 2 2 2 3 4 0 0 t t u u i i h K H aXaXaXaXaXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaXaXaXaXaXaX- - : - - & & & & & & & & & & & - ; 2 2 1 3 5 4 4 0 t t t h z h j aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaX- - : - - & & & & & & & & & & - ; ; 2 2 1 4 3 4 t y z u u aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaX- - : : - & & & & & & & & - - - : 2 2 5 4 y y t t aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaX- - & ; ; ; ; - - - & - : 2 3 3 3 3 5 aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaX- & & & & & & & - aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaX", +"aXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaXaX" }; diff --git a/share/pixmaps/favicon.ico b/share/pixmaps/favicon.ico index 754eebc4..d018174f 100644 Binary files a/share/pixmaps/favicon.ico and b/share/pixmaps/favicon.ico differ diff --git a/share/pixmaps/nsis-header.bmp b/share/pixmaps/nsis-header.bmp index 9ab0ce25..43a2e04e 100644 Binary files a/share/pixmaps/nsis-header.bmp and b/share/pixmaps/nsis-header.bmp differ diff --git a/share/pixmaps/nsis-wizard.bmp b/share/pixmaps/nsis-wizard.bmp index 71255c68..64f871a4 100644 Binary files a/share/pixmaps/nsis-wizard.bmp and b/share/pixmaps/nsis-wizard.bmp differ diff --git a/share/qt/Info.plist.in b/share/qt/Info.plist.in new file mode 100644 index 00000000..874ed031 --- /dev/null +++ b/share/qt/Info.plist.in @@ -0,0 +1,69 @@ + + + + + LSMinimumSystemVersion + 10.12.0 + + LSArchitecturePriority + + x86_64 + + + CFBundleIconFile + komodo.icns + + CFBundlePackageType + APPL + + NSHumanReadableCopyright + @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@.@CLIENT_VERSION_BUILD@, Copyright © 2009-@COPYRIGHT_YEAR@ @COPYRIGHT_HOLDERS_SUBSTITUTION@ + + CFBundleShortVersionString + @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@ + + CFBundleVersion + @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@ + + CFBundleSignature + ???? + + CFBundleExecutable + Komodo-Qt + + CFBundleName + Komodo-Qt + + LSHasLocalizedDisplayName + + + CFBundleIdentifier + org.komodoplatform.Komodo-Qt + + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + org.komodoplatform.KomodoPayment + CFBundleURLSchemes + + komodo + + + + + NSPrincipalClass + NSApplication + + NSHighResolutionCapable + True + + NSRequiresAquaSystemAppearance + True + + LSApplicationCategoryType + public.app-category.finance + + diff --git a/share/qt/extract_strings_qt.py b/share/qt/extract_strings_qt.py new file mode 100755 index 00000000..1bb49f4e --- /dev/null +++ b/share/qt/extract_strings_qt.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# Copyright (c) 2012-2019 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +''' +Extract _("...") strings for translation and convert to Qt stringdefs so that +they can be picked up by Qt linguist. +''' +from subprocess import Popen, PIPE +import operator +import os +import sys + +OUT_CPP="qt/komodostrings.cpp" +EMPTY=['""'] + +def parse_po(text): + """ + Parse 'po' format produced by xgettext. + Return a list of (msgid,msgstr) tuples. + """ + messages = [] + msgid = [] + msgstr = [] + in_msgid = False + in_msgstr = False + + for line in text.split('\n'): + line = line.rstrip('\r') + if line.startswith('msgid '): + if in_msgstr: + messages.append((msgid, msgstr)) + in_msgstr = False + # message start + in_msgid = True + + msgid = [line[6:]] + elif line.startswith('msgstr '): + in_msgid = False + in_msgstr = True + msgstr = [line[7:]] + elif line.startswith('"'): + if in_msgid: + msgid.append(line) + if in_msgstr: + msgstr.append(line) + + if in_msgstr: + messages.append((msgid, msgstr)) + + return messages + +files = sys.argv[1:] + +# xgettext -n --keyword=_ $FILES +XGETTEXT=os.getenv('XGETTEXT', 'xgettext') +if not XGETTEXT: + print('Cannot extract strings: xgettext utility is not installed or not configured.',file=sys.stderr) + print('Please install package "gettext" and re-run \'./configure\'.',file=sys.stderr) + sys.exit(1) +child = Popen([XGETTEXT,'--output=-','-n','--keyword=_'] + files, stdout=PIPE) +(out, err) = child.communicate() + +messages = parse_po(out.decode('utf-8')) + +f = open(OUT_CPP, 'w', encoding="utf8") +f.write(""" + +#include + +// Automatically generated by extract_strings_qt.py +#ifdef __GNUC__ +#define UNUSED __attribute__((unused)) +#else +#define UNUSED +#endif +""") +f.write('static const char UNUSED *komodo_strings[] = {\n') +f.write('QT_TRANSLATE_NOOP("komodo-core", "%s"),\n' % (os.getenv('COPYRIGHT_HOLDERS'),)) +messages.sort(key=operator.itemgetter(0)) +for (msgid, msgstr) in messages: + if msgid != EMPTY: + f.write('QT_TRANSLATE_NOOP("komodo-core", %s),\n' % ('\n'.join(msgid))) +f.write('};\n') +f.close() diff --git a/share/setup.nsi.in b/share/setup.nsi.in index 6c0e895b..2b8e37b8 100644 --- a/share/setup.nsi.in +++ b/share/setup.nsi.in @@ -5,9 +5,9 @@ SetCompressor /SOLID lzma # General Symbol Definitions !define REGKEY "SOFTWARE\$(^Name)" -!define VERSION @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@ -!define COMPANY "Bitcoin Core project" -!define URL http://www.bitcoin.org/ +!define VERSION @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_REVISION@.@CLIENT_VERSION_BUILD@ +!define COMPANY "KomodoOcean project" +!define URL https://komodoplatform.com/ # MUI Symbol Definitions !define MUI_ICON "@abs_top_srcdir@/share/pixmaps/bitcoin.ico" @@ -20,7 +20,7 @@ SetCompressor /SOLID lzma !define MUI_STARTMENUPAGE_REGISTRY_KEY ${REGKEY} !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME StartMenuGroup !define MUI_STARTMENUPAGE_DEFAULTFOLDER "@PACKAGE_NAME@" -!define MUI_FINISHPAGE_RUN $INSTDIR\bitcoin-qt.exe +!define MUI_FINISHPAGE_RUN $INSTDIR\komodo-qt.exe !define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico" !define MUI_UNWELCOMEFINISHPAGE_BITMAP "@abs_top_srcdir@/share/pixmaps/nsis-wizard.bmp" !define MUI_UNFINISHPAGE_NOAUTOCLOSE @@ -48,18 +48,18 @@ Var StartMenuGroup !insertmacro MUI_LANGUAGE English # Installer attributes -OutFile @abs_top_srcdir@/bitcoin-${VERSION}-win@WINDOWS_BITS@-setup.exe +OutFile @abs_top_srcdir@/KomodoOcean-${VERSION}-win@WINDOWS_BITS@-setup.exe !if "@WINDOWS_BITS@" == "64" -InstallDir $PROGRAMFILES64\Bitcoin +InstallDir $PROGRAMFILES64\KomodoOcean !else -InstallDir $PROGRAMFILES\Bitcoin +InstallDir $PROGRAMFILES\KomodoOcean !endif CRCCheck on XPStyle on BrandingText " " ShowInstDetails show VIProductVersion ${VERSION}.@CLIENT_VERSION_BUILD@ -VIAddVersionKey ProductName "Bitcoin Core" +VIAddVersionKey ProductName "KomodoOcean" VIAddVersionKey ProductVersion "${VERSION}" VIAddVersionKey CompanyName "${COMPANY}" VIAddVersionKey CompanyWebsite "${URL}" @@ -73,19 +73,18 @@ ShowUninstDetails show Section -Main SEC0000 SetOutPath $INSTDIR SetOverwrite on - File @abs_top_srcdir@/release/bitcoin-qt.exe - File /oname=COPYING.txt @abs_top_srcdir@/COPYING - File /oname=readme.txt @abs_top_srcdir@/doc/README_windows.txt + File @abs_top_srcdir@/release/komodo-qt.exe + # File /oname=COPYING.txt @abs_top_srcdir@/COPYING + # File /oname=readme.txt @abs_top_srcdir@/doc/README_windows.txt SetOutPath $INSTDIR\daemon - File @abs_top_srcdir@/release/bitcoind.exe - File @abs_top_srcdir@/release/bitcoin-cli.exe + File @abs_top_srcdir@/release/komodo-cli.exe SetOutPath $INSTDIR\doc File /r @abs_top_srcdir@/doc\*.* SetOutPath $INSTDIR WriteRegStr HKCU "${REGKEY}\Components" Main 1 # Remove old wxwidgets-based-bitcoin executable and locales: - Delete /REBOOTOK $INSTDIR\bitcoin.exe + # Delete /REBOOTOK $INSTDIR\bitcoin.exe RMDir /r /REBOOTOK $INSTDIR\locale SectionEnd @@ -95,7 +94,7 @@ Section -post SEC0001 WriteUninstaller $INSTDIR\uninstall.exe !insertmacro MUI_STARTMENU_WRITE_BEGIN Application CreateDirectory $SMPROGRAMS\$StartMenuGroup - CreateShortcut "$SMPROGRAMS\$StartMenuGroup\$(^Name).lnk" $INSTDIR\bitcoin-qt.exe + CreateShortcut "$SMPROGRAMS\$StartMenuGroup\$(^Name).lnk" $INSTDIR\komodo-qt.exe CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall $(^Name).lnk" $INSTDIR\uninstall.exe !insertmacro MUI_STARTMENU_WRITE_END WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayName "$(^Name)" @@ -106,10 +105,10 @@ Section -post SEC0001 WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" UninstallString $INSTDIR\uninstall.exe WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoModify 1 WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoRepair 1 - WriteRegStr HKCR "bitcoin" "URL Protocol" "" - WriteRegStr HKCR "bitcoin" "" "URL:Bitcoin" - WriteRegStr HKCR "bitcoin\DefaultIcon" "" $INSTDIR\bitcoin-qt.exe - WriteRegStr HKCR "bitcoin\shell\open\command" "" '"$INSTDIR\bitcoin-qt.exe" "%1"' + WriteRegStr HKCR "komodo" "URL Protocol" "" + WriteRegStr HKCR "komodo" "" "URL:Komodo" + WriteRegStr HKCR "komodo\DefaultIcon" "" $INSTDIR\komodo-qt.exe + WriteRegStr HKCR "komodo\shell\open\command" "" '"$INSTDIR\komodo-qt.exe" "%1"' SectionEnd # Macro for selecting uninstaller sections @@ -127,7 +126,7 @@ done${UNSECTION_ID}: # Uninstaller sections Section /o -un.Main UNSEC0000 - Delete /REBOOTOK $INSTDIR\bitcoin-qt.exe + Delete /REBOOTOK $INSTDIR\komodo-qt.exe Delete /REBOOTOK $INSTDIR\COPYING.txt Delete /REBOOTOK $INSTDIR\readme.txt RMDir /r /REBOOTOK $INSTDIR\daemon @@ -139,7 +138,7 @@ Section -un.post UNSEC0001 DeleteRegKey HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\Uninstall $(^Name).lnk" Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\$(^Name).lnk" - Delete /REBOOTOK "$SMSTARTUP\Bitcoin.lnk" + Delete /REBOOTOK "$SMSTARTUP\Komodo.lnk" Delete /REBOOTOK $INSTDIR\uninstall.exe Delete /REBOOTOK $INSTDIR\debug.log Delete /REBOOTOK $INSTDIR\db.log @@ -147,7 +146,7 @@ Section -un.post UNSEC0001 DeleteRegValue HKCU "${REGKEY}" Path DeleteRegKey /IfEmpty HKCU "${REGKEY}\Components" DeleteRegKey /IfEmpty HKCU "${REGKEY}" - DeleteRegKey HKCR "bitcoin" + DeleteRegKey HKCR "komodo" RmDir /REBOOTOK $SMPROGRAMS\$StartMenuGroup RmDir /REBOOTOK $INSTDIR Push $R0 diff --git a/src/Makefile.am b/src/Makefile.am index 1996bcdb..1cbf956c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,21 +5,6 @@ AM_CXXFLAGS = $(SAN_CXXFLAGS) $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) AM_CPPFLAGS = $(HARDENED_CPPFLAGS) EXTRA_LIBRARIES = -if EMBEDDED_LEVELDB -LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/include -LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/helpers/memenv -LIBLEVELDB += $(builddir)/leveldb/libleveldb.a -LIBMEMENV += $(builddir)/leveldb/libmemenv.a - -# NOTE: This dependency is not strictly necessary, but without it make may try to build both in parallel, which breaks the LevelDB build system in a race -$(LIBLEVELDB): $(LIBMEMENV) - -$(LIBLEVELDB) $(LIBMEMENV): - @echo "Building LevelDB ..." && $(MAKE) -C $(@D) $(@F) CXX="$(CXX)" \ - CC="$(CC)" PLATFORM=$(TARGET_OS) AR="$(AR)" $(LEVELDB_TARGET_FLAGS) \ - OPT="$(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) -D__STDC_LIMIT_MACROS" -endif - BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS) @@ -75,12 +60,15 @@ LIBSNARK_CONFIG_FLAGS = CURVE=ALT_BN128 NO_PROCPS=1 NO_DOCS=1 STATIC=1 NO_SUPERC if HAVE_OPENMP LIBSNARK_CONFIG_FLAGS += MULTICORE=1 endif +if TARGET_DARWIN +LIBSNARK_CONFIG_FLAGS += PLATFORM=darwin +endif $(LIBSNARK): $(wildcard snark/src/*) - $(AM_V_at) CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="-O2 -march=x86-64" + $(AM_V_at) CC="$(CC)" CXX="$(CXX)" AR="$(AR)" CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="-O2 -march=x86-64" libsnark-tests: $(wildcard snark/src/*) - $(AM_V_at) CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ check DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="-O2 -march=x86-64" + $(AM_V_at) CC="$(CC)" CXX="$(CXX)" AR="$(AR)" CXXFLAGS="$(LIBSNARK_CXXFLAGS)" $(MAKE) $(AM_MAKEFLAGS) -C snark/ check DEPINST="$(LIBSNARK_DEPINST)" $(LIBSNARK_CONFIG_FLAGS) OPTFLAGS="-O2 -march=x86-64" $(LIBUNIVALUE): $(wildcard univalue/lib/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue/ @@ -574,6 +562,7 @@ komodod_LDADD = \ $(LIBZCASH) \ $(LIBSNARK) \ $(LIBLEVELDB) \ + $(LIBLEVELDB_SSE42) \ $(LIBMEMENV) \ $(LIBSECP256K1) \ $(LIBCRYPTOCONDITIONS) @@ -672,7 +661,10 @@ libzcash_a_SOURCES = \ zcash/circuit/prfs.tcc \ zcash/circuit/utils.tcc -libzcash_a_CPPFLAGS = -DMULTICORE -fopenmp -fPIC -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS $(HARDENED_CPPFLAGS) -pipe -O1 -g -Wstack-protector -fstack-protector-all -fPIE -fvisibility=hidden -DSTATIC $(BITCOIN_INCLUDES) +libzcash_a_CPPFLAGS = -fPIC -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS $(HARDENED_CPPFLAGS) -pipe -O1 -g -Wstack-protector -fstack-protector-all -fPIE -fvisibility=hidden -DSTATIC $(BITCOIN_INCLUDES) +if HAVE_OPENMP +libzcash_a_CPPFLAGS += -DMULTICORE -fopenmp +endif libzcash_a_CXXFLAGS = $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing @@ -682,7 +674,7 @@ libzcash_a_CPPFLAGS += -DMONTGOMERY_OUTPUT # zcashconsensus library # if BUILD_KOMODO_LIBS -include_HEADERS = +include_HEADERS = script/zcashconsensus.h libzcashconsensus_la_SOURCES = \ crypto/equihash.cpp \ crypto/hmac_sha512.cpp \ @@ -704,25 +696,25 @@ if GLIBC_BACK_COMPAT libzcashconsensus_la_SOURCES += compat/glibc_compat.cpp endif -libzcashconsensus_la_LDFLAGS = -no-undefined $(RELDFLAGS) +libzcashconsensus_la_LDFLAGS = -no-undefined $(RELDFLAGS) -static libzcashconsensus_la_LIBADD = $(LIBSECP256K1) -libzcashconsensus_la_LIBADD = $(CRYPTO_LIBS) -libzcashconsensus_la_CPPFLAGS = $(CRYPTO_CFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -I$(srcdir)/cryptoconditions/include -DBUILD_BITCOIN_INTERNAL - +libzcashconsensus_la_CPPFLAGS = $(CRYPTO_CFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -I$(srcdir)/cryptoconditions/include -DBUILD_BITCOIN_INTERNAL -fvisibility=hidden endif # -CLEANFILES = leveldb/libleveldb.a leveldb/libmemenv.a *.gcda *.gcno */*.gcno wallet/*/*.gcno - +CLEANFILES = $(LIBBITCOIN_COMMON) libbitcoin_server.a $(LIBBITCOIN_CLI) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_WALLET) $(LIBBITCOIN_ZMQ) libzcash.a +CLEANFILES += $(LIBBITCOIN_CRYPTO) $(LIBVERUS_CRYPTO) $(LIBVERUS_PORTABLE_CRYPTO) +CLEANFILES += *.gcda *.gcno */*.gcno wallet/*/*.gcno DISTCLEANFILES = obj/build.h -EXTRA_DIST = leveldb snark +EXTRA_DIST = snark clean-local: - -$(MAKE) -C leveldb clean -$(MAKE) -C secp256k1 clean -$(MAKE) -C snark clean - rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno + -$(MAKE) -C univalue clean + -$(MAKE) -C cryptoconditions clean + -rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno -rm -f config.h .rc.o: @@ -751,6 +743,10 @@ if ENABLE_BIP70 $(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(abspath $( #include +#include #include #include #include diff --git a/src/init.cpp b/src/init.cpp index 45065c33..2e4f943c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1336,6 +1336,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) << BOOST_VERSION % 100; // patch level LogPrintf("Using Boost version %s\n", boost_version_ss.str() /*BOOST_LIB_VERSION*/ ); LogPrintf("Using Sodium version %s\n", sodium_version_string()); + LogPrintf("Using LevelDB version %d.%d\n", leveldb::kMajorVersion, leveldb::kMinorVersion); if (!fLogTimestamps) LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime())); diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index d663227e..43b86b78 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -516,7 +516,7 @@ int32_t komodo_verifynotarization(char *symbol,char *dest,int32_t height,int32_t { if ( (json= cJSON_Parse(jsonstr)) != 0 ) { - if ( (txjson= jobj(json,(char *)"result")) != 0 && (vouts= jarray(&n,txjson,(char *)"vout")) > 0 ) + if ( (txjson= jobj(json,(char *)"result")) != 0 && (vouts= jarray(&n,txjson,(char *)"vout")) != 0 ) { vout = jitem(vouts,n-1); if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) diff --git a/src/leveldb/.travis.yml b/src/leveldb/.travis.yml new file mode 100644 index 00000000..f5bd74c4 --- /dev/null +++ b/src/leveldb/.travis.yml @@ -0,0 +1,13 @@ +language: cpp +compiler: +- clang +- gcc +os: +- linux +- osx +sudo: false +before_install: +- echo $LANG +- echo $LC_ALL +script: +- make -j 4 check diff --git a/src/leveldb/Makefile b/src/leveldb/Makefile index 2bd2cadc..f7cc7d73 100644 --- a/src/leveldb/Makefile +++ b/src/leveldb/Makefile @@ -20,208 +20,405 @@ $(shell CC="$(CC)" CXX="$(CXX)" TARGET_OS="$(TARGET_OS)" \ # this file is generated by the previous line to set build flags and sources include build_config.mk +TESTS = \ + db/autocompact_test \ + db/c_test \ + db/corruption_test \ + db/db_test \ + db/dbformat_test \ + db/fault_injection_test \ + db/filename_test \ + db/log_test \ + db/recovery_test \ + db/skiplist_test \ + db/version_edit_test \ + db/version_set_test \ + db/write_batch_test \ + helpers/memenv/memenv_test \ + issues/issue178_test \ + issues/issue200_test \ + table/filter_block_test \ + table/table_test \ + util/arena_test \ + util/bloom_test \ + util/cache_test \ + util/coding_test \ + util/crc32c_test \ + util/env_posix_test \ + util/env_test \ + util/hash_test + +UTILS = \ + db/db_bench \ + db/leveldbutil + +# Put the object files in a subdirectory, but the application at the top of the object dir. +PROGNAMES := $(notdir $(TESTS) $(UTILS)) + +# On Linux may need libkyotocabinet-dev for dependency. +BENCHMARKS = \ + doc/bench/db_bench_sqlite3 \ + doc/bench/db_bench_tree_db + CFLAGS += -I. -I./include $(PLATFORM_CCFLAGS) $(OPT) CXXFLAGS += -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) LDFLAGS += $(PLATFORM_LDFLAGS) LIBS += $(PLATFORM_LIBS) -LIBOBJECTS = $(SOURCES:.cc=.o) -MEMENVOBJECTS = $(MEMENV_SOURCES:.cc=.o) - -TESTUTIL = ./util/testutil.o -TESTHARNESS = ./util/testharness.o $(TESTUTIL) +SIMULATOR_OUTDIR=out-ios-x86 +DEVICE_OUTDIR=out-ios-arm -# Note: iOS should probably be using libtool, not ar. ifeq ($(PLATFORM), IOS) +# Note: iOS should probably be using libtool, not ar. AR=xcrun ar +SIMULATORSDK=$(shell xcrun -sdk iphonesimulator --show-sdk-path) +DEVICESDK=$(shell xcrun -sdk iphoneos --show-sdk-path) +DEVICE_CFLAGS = -isysroot "$(DEVICESDK)" -arch armv6 -arch armv7 -arch armv7s -arch arm64 +SIMULATOR_CFLAGS = -isysroot "$(SIMULATORSDK)" -arch i686 -arch x86_64 +STATIC_OUTDIR=out-ios-universal +else +STATIC_OUTDIR=out-static +SHARED_OUTDIR=out-shared +STATIC_PROGRAMS := $(addprefix $(STATIC_OUTDIR)/, $(PROGNAMES)) +SHARED_PROGRAMS := $(addprefix $(SHARED_OUTDIR)/, db_bench) endif -TESTS = \ - arena_test \ - autocompact_test \ - bloom_test \ - c_test \ - cache_test \ - coding_test \ - corruption_test \ - crc32c_test \ - db_test \ - dbformat_test \ - env_test \ - filename_test \ - filter_block_test \ - hash_test \ - issue178_test \ - issue200_test \ - log_test \ - memenv_test \ - skiplist_test \ - table_test \ - version_edit_test \ - version_set_test \ - write_batch_test - -PROGRAMS = db_bench leveldbutil $(TESTS) -BENCHMARKS = db_bench_sqlite3 db_bench_tree_db - -LIBRARY = libleveldb.a -MEMENVLIBRARY = libmemenv.a +STATIC_LIBOBJECTS := $(addprefix $(STATIC_OUTDIR)/, $(SOURCES:.cc=.o)) +STATIC_MEMENVOBJECTS := $(addprefix $(STATIC_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o)) + +DEVICE_LIBOBJECTS := $(addprefix $(DEVICE_OUTDIR)/, $(SOURCES:.cc=.o)) +DEVICE_MEMENVOBJECTS := $(addprefix $(DEVICE_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o)) + +SIMULATOR_LIBOBJECTS := $(addprefix $(SIMULATOR_OUTDIR)/, $(SOURCES:.cc=.o)) +SIMULATOR_MEMENVOBJECTS := $(addprefix $(SIMULATOR_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o)) + +SHARED_LIBOBJECTS := $(addprefix $(SHARED_OUTDIR)/, $(SOURCES:.cc=.o)) +SHARED_MEMENVOBJECTS := $(addprefix $(SHARED_OUTDIR)/, $(MEMENV_SOURCES:.cc=.o)) + +TESTUTIL := $(STATIC_OUTDIR)/util/testutil.o +TESTHARNESS := $(STATIC_OUTDIR)/util/testharness.o $(TESTUTIL) + +STATIC_TESTOBJS := $(addprefix $(STATIC_OUTDIR)/, $(addsuffix .o, $(TESTS))) +STATIC_UTILOBJS := $(addprefix $(STATIC_OUTDIR)/, $(addsuffix .o, $(UTILS))) +STATIC_ALLOBJS := $(STATIC_LIBOBJECTS) $(STATIC_MEMENVOBJECTS) $(STATIC_TESTOBJS) $(STATIC_UTILOBJS) $(TESTHARNESS) +DEVICE_ALLOBJS := $(DEVICE_LIBOBJECTS) $(DEVICE_MEMENVOBJECTS) +SIMULATOR_ALLOBJS := $(SIMULATOR_LIBOBJECTS) $(SIMULATOR_MEMENVOBJECTS) default: all # Should we build shared libraries? ifneq ($(PLATFORM_SHARED_EXT),) +# Many leveldb test apps use non-exported API's. Only build a subset for testing. +SHARED_ALLOBJS := $(SHARED_LIBOBJECTS) $(SHARED_MEMENVOBJECTS) $(TESTHARNESS) + ifneq ($(PLATFORM_SHARED_VERSIONED),true) -SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) -SHARED2 = $(SHARED1) -SHARED3 = $(SHARED1) -SHARED = $(SHARED1) +SHARED_LIB1 = libleveldb.$(PLATFORM_SHARED_EXT) +SHARED_LIB2 = $(SHARED_LIB1) +SHARED_LIB3 = $(SHARED_LIB1) +SHARED_LIBS = $(SHARED_LIB1) +SHARED_MEMENVLIB = $(SHARED_OUTDIR)/libmemenv.a else # Update db.h if you change these. -SHARED_MAJOR = 1 -SHARED_MINOR = 18 -SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) -SHARED2 = $(SHARED1).$(SHARED_MAJOR) -SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR) -SHARED = $(SHARED1) $(SHARED2) $(SHARED3) -$(SHARED1): $(SHARED3) - ln -fs $(SHARED3) $(SHARED1) -$(SHARED2): $(SHARED3) - ln -fs $(SHARED3) $(SHARED2) +SHARED_VERSION_MAJOR = 1 +SHARED_VERSION_MINOR = 20 +SHARED_LIB1 = libleveldb.$(PLATFORM_SHARED_EXT) +SHARED_LIB2 = $(SHARED_LIB1).$(SHARED_VERSION_MAJOR) +SHARED_LIB3 = $(SHARED_LIB1).$(SHARED_VERSION_MAJOR).$(SHARED_VERSION_MINOR) +SHARED_LIBS = $(SHARED_OUTDIR)/$(SHARED_LIB1) $(SHARED_OUTDIR)/$(SHARED_LIB2) $(SHARED_OUTDIR)/$(SHARED_LIB3) +$(SHARED_OUTDIR)/$(SHARED_LIB1): $(SHARED_OUTDIR)/$(SHARED_LIB3) + ln -fs $(SHARED_LIB3) $(SHARED_OUTDIR)/$(SHARED_LIB1) +$(SHARED_OUTDIR)/$(SHARED_LIB2): $(SHARED_OUTDIR)/$(SHARED_LIB3) + ln -fs $(SHARED_LIB3) $(SHARED_OUTDIR)/$(SHARED_LIB2) +SHARED_MEMENVLIB = $(SHARED_OUTDIR)/libmemenv.a endif -$(SHARED3): - $(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) -o $(SHARED3) $(LIBS) +$(SHARED_OUTDIR)/$(SHARED_LIB3): $(SHARED_LIBOBJECTS) + $(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED_LIB2) $(SHARED_LIBOBJECTS) -o $(SHARED_OUTDIR)/$(SHARED_LIB3) $(LIBS) endif # PLATFORM_SHARED_EXT -all: $(SHARED) $(LIBRARY) +all: $(SHARED_LIBS) $(SHARED_PROGRAMS) $(STATIC_OUTDIR)/libleveldb.a $(STATIC_OUTDIR)/libmemenv.a $(STATIC_PROGRAMS) -check: all $(PROGRAMS) $(TESTS) - for t in $(TESTS); do echo "***** Running $$t"; ./$$t || exit 1; done +check: $(STATIC_PROGRAMS) + for t in $(notdir $(TESTS)); do echo "***** Running $$t"; $(STATIC_OUTDIR)/$$t || exit 1; done clean: - -rm -f $(PROGRAMS) $(BENCHMARKS) $(LIBRARY) $(SHARED) $(MEMENVLIBRARY) */*.o */*/*.o ios-x86/*/*.o ios-arm/*/*.o build_config.mk - -rm -rf ios-x86/* ios-arm/* + -rm -rf out-static out-shared out-ios-x86 out-ios-arm out-ios-universal + -rm -f build_config.mk + -rm -rf ios-x86 ios-arm -$(LIBRARY): $(LIBOBJECTS) - rm -f $@ - $(AR) -rs $@ $(LIBOBJECTS) +$(STATIC_OUTDIR): + mkdir $@ -db_bench: db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) - $(CXX) $(LDFLAGS) db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LIBS) +$(STATIC_OUTDIR)/db: | $(STATIC_OUTDIR) + mkdir $@ -db_bench_sqlite3: doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) - $(CXX) $(LDFLAGS) doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) -o $@ -lsqlite3 $(LIBS) +$(STATIC_OUTDIR)/helpers/memenv: | $(STATIC_OUTDIR) + mkdir -p $@ -db_bench_tree_db: doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) - $(CXX) $(LDFLAGS) doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) -o $@ -lkyotocabinet $(LIBS) +$(STATIC_OUTDIR)/port: | $(STATIC_OUTDIR) + mkdir $@ -leveldbutil: db/leveldb_main.o $(LIBOBJECTS) - $(CXX) $(LDFLAGS) db/leveldb_main.o $(LIBOBJECTS) -o $@ $(LIBS) +$(STATIC_OUTDIR)/table: | $(STATIC_OUTDIR) + mkdir $@ -arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(STATIC_OUTDIR)/util: | $(STATIC_OUTDIR) + mkdir $@ -autocompact_test: db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +.PHONY: STATIC_OBJDIRS +STATIC_OBJDIRS: \ + $(STATIC_OUTDIR)/db \ + $(STATIC_OUTDIR)/port \ + $(STATIC_OUTDIR)/table \ + $(STATIC_OUTDIR)/util \ + $(STATIC_OUTDIR)/helpers/memenv -bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(SHARED_OUTDIR): + mkdir $@ -c_test: db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(SHARED_OUTDIR)/db: | $(SHARED_OUTDIR) + mkdir $@ -cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(SHARED_OUTDIR)/helpers/memenv: | $(SHARED_OUTDIR) + mkdir -p $@ -coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(SHARED_OUTDIR)/port: | $(SHARED_OUTDIR) + mkdir $@ -corruption_test: db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(SHARED_OUTDIR)/table: | $(SHARED_OUTDIR) + mkdir $@ -crc32c_test: util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(SHARED_OUTDIR)/util: | $(SHARED_OUTDIR) + mkdir $@ -db_test: db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +.PHONY: SHARED_OBJDIRS +SHARED_OBJDIRS: \ + $(SHARED_OUTDIR)/db \ + $(SHARED_OUTDIR)/port \ + $(SHARED_OUTDIR)/table \ + $(SHARED_OUTDIR)/util \ + $(SHARED_OUTDIR)/helpers/memenv -dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(DEVICE_OUTDIR): + mkdir $@ -env_test: util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(DEVICE_OUTDIR)/db: | $(DEVICE_OUTDIR) + mkdir $@ -filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(DEVICE_OUTDIR)/helpers/memenv: | $(DEVICE_OUTDIR) + mkdir -p $@ -filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(DEVICE_OUTDIR)/port: | $(DEVICE_OUTDIR) + mkdir $@ -hash_test: util/hash_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) util/hash_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(DEVICE_OUTDIR)/table: | $(DEVICE_OUTDIR) + mkdir $@ -issue178_test: issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(DEVICE_OUTDIR)/util: | $(DEVICE_OUTDIR) + mkdir $@ -issue200_test: issues/issue200_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) issues/issue200_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +.PHONY: DEVICE_OBJDIRS +DEVICE_OBJDIRS: \ + $(DEVICE_OUTDIR)/db \ + $(DEVICE_OUTDIR)/port \ + $(DEVICE_OUTDIR)/table \ + $(DEVICE_OUTDIR)/util \ + $(DEVICE_OUTDIR)/helpers/memenv -log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(SIMULATOR_OUTDIR): + mkdir $@ -table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(SIMULATOR_OUTDIR)/db: | $(SIMULATOR_OUTDIR) + mkdir $@ -skiplist_test: db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(SIMULATOR_OUTDIR)/helpers/memenv: | $(SIMULATOR_OUTDIR) + mkdir -p $@ -version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(SIMULATOR_OUTDIR)/port: | $(SIMULATOR_OUTDIR) + mkdir $@ -version_set_test: db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(SIMULATOR_OUTDIR)/table: | $(SIMULATOR_OUTDIR) + mkdir $@ -write_batch_test: db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +$(SIMULATOR_OUTDIR)/util: | $(SIMULATOR_OUTDIR) + mkdir $@ -$(MEMENVLIBRARY) : $(MEMENVOBJECTS) - rm -f $@ - $(AR) -rs $@ $(MEMENVOBJECTS) +.PHONY: SIMULATOR_OBJDIRS +SIMULATOR_OBJDIRS: \ + $(SIMULATOR_OUTDIR)/db \ + $(SIMULATOR_OUTDIR)/port \ + $(SIMULATOR_OUTDIR)/table \ + $(SIMULATOR_OUTDIR)/util \ + $(SIMULATOR_OUTDIR)/helpers/memenv -memenv_test : helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) - $(CXX) $(LDFLAGS) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(LIBS) +$(STATIC_ALLOBJS): | STATIC_OBJDIRS +$(DEVICE_ALLOBJS): | DEVICE_OBJDIRS +$(SIMULATOR_ALLOBJS): | SIMULATOR_OBJDIRS +$(SHARED_ALLOBJS): | SHARED_OBJDIRS ifeq ($(PLATFORM), IOS) -# For iOS, create universal object files to be used on both the simulator and +$(DEVICE_OUTDIR)/libleveldb.a: $(DEVICE_LIBOBJECTS) + rm -f $@ + $(AR) -rs $@ $(DEVICE_LIBOBJECTS) + +$(SIMULATOR_OUTDIR)/libleveldb.a: $(SIMULATOR_LIBOBJECTS) + rm -f $@ + $(AR) -rs $@ $(SIMULATOR_LIBOBJECTS) + +$(DEVICE_OUTDIR)/libmemenv.a: $(DEVICE_MEMENVOBJECTS) + rm -f $@ + $(AR) -rs $@ $(DEVICE_MEMENVOBJECTS) + +$(SIMULATOR_OUTDIR)/libmemenv.a: $(SIMULATOR_MEMENVOBJECTS) + rm -f $@ + $(AR) -rs $@ $(SIMULATOR_MEMENVOBJECTS) + +# For iOS, create universal object libraries to be used on both the simulator and # a device. -PLATFORMSROOT=/Applications/Xcode.app/Contents/Developer/Platforms -SIMULATORROOT=$(PLATFORMSROOT)/iPhoneSimulator.platform/Developer -DEVICEROOT=$(PLATFORMSROOT)/iPhoneOS.platform/Developer -IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBundleShortVersionString) -IOSARCH=-arch armv6 -arch armv7 -arch armv7s -arch arm64 - -.cc.o: - mkdir -p ios-x86/$(dir $@) - xcrun -sdk iphonesimulator $(CXX) $(CXXFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -arch x86_64 -c $< -o ios-x86/$@ - mkdir -p ios-arm/$(dir $@) - xcrun -sdk iphoneos $(CXX) $(CXXFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk $(IOSARCH) -c $< -o ios-arm/$@ - xcrun lipo ios-x86/$@ ios-arm/$@ -create -output $@ - -.c.o: - mkdir -p ios-x86/$(dir $@) - xcrun -sdk iphonesimulator $(CC) $(CFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -arch x86_64 -c $< -o ios-x86/$@ - mkdir -p ios-arm/$(dir $@) - xcrun -sdk iphoneos $(CC) $(CFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk $(IOSARCH) -c $< -o ios-arm/$@ - xcrun lipo ios-x86/$@ ios-arm/$@ -create -output $@ +$(STATIC_OUTDIR)/libleveldb.a: $(STATIC_OUTDIR) $(DEVICE_OUTDIR)/libleveldb.a $(SIMULATOR_OUTDIR)/libleveldb.a + lipo -create $(DEVICE_OUTDIR)/libleveldb.a $(SIMULATOR_OUTDIR)/libleveldb.a -output $@ +$(STATIC_OUTDIR)/libmemenv.a: $(STATIC_OUTDIR) $(DEVICE_OUTDIR)/libmemenv.a $(SIMULATOR_OUTDIR)/libmemenv.a + lipo -create $(DEVICE_OUTDIR)/libmemenv.a $(SIMULATOR_OUTDIR)/libmemenv.a -output $@ else -.cc.o: +$(STATIC_OUTDIR)/libleveldb.a:$(STATIC_LIBOBJECTS) + rm -f $@ + $(AR) -rs $@ $(STATIC_LIBOBJECTS) + +$(STATIC_OUTDIR)/libmemenv.a:$(STATIC_MEMENVOBJECTS) + rm -f $@ + $(AR) -rs $@ $(STATIC_MEMENVOBJECTS) +endif + +$(SHARED_MEMENVLIB):$(SHARED_MEMENVOBJECTS) + rm -f $@ + $(AR) -rs $@ $(SHARED_MEMENVOBJECTS) + +$(STATIC_OUTDIR)/db_bench:db/db_bench.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) + $(CXX) $(LDFLAGS) $(CXXFLAGS) db/db_bench.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/db_bench_sqlite3:doc/bench/db_bench_sqlite3.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) + $(CXX) $(LDFLAGS) $(CXXFLAGS) doc/bench/db_bench_sqlite3.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) -o $@ -lsqlite3 $(LIBS) + +$(STATIC_OUTDIR)/db_bench_tree_db:doc/bench/db_bench_tree_db.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) + $(CXX) $(LDFLAGS) $(CXXFLAGS) doc/bench/db_bench_tree_db.cc $(STATIC_LIBOBJECTS) $(TESTUTIL) -o $@ -lkyotocabinet $(LIBS) + +$(STATIC_OUTDIR)/leveldbutil:db/leveldbutil.cc $(STATIC_LIBOBJECTS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) db/leveldbutil.cc $(STATIC_LIBOBJECTS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/arena_test:util/arena_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) util/arena_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/autocompact_test:db/autocompact_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) db/autocompact_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/bloom_test:util/bloom_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) util/bloom_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/c_test:$(STATIC_OUTDIR)/db/c_test.o $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(STATIC_OUTDIR)/db/c_test.o $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/cache_test:util/cache_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) util/cache_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/coding_test:util/coding_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) util/coding_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/corruption_test:db/corruption_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) db/corruption_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/crc32c_test:util/crc32c_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) util/crc32c_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/db_test:db/db_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) db/db_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/dbformat_test:db/dbformat_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) db/dbformat_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/env_posix_test:util/env_posix_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) util/env_posix_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/env_test:util/env_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) util/env_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/fault_injection_test:db/fault_injection_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) db/fault_injection_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/filename_test:db/filename_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) db/filename_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/filter_block_test:table/filter_block_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) table/filter_block_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/hash_test:util/hash_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) util/hash_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/issue178_test:issues/issue178_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) issues/issue178_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/issue200_test:issues/issue200_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) issues/issue200_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/log_test:db/log_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) db/log_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/recovery_test:db/recovery_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) db/recovery_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/table_test:table/table_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) table/table_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/skiplist_test:db/skiplist_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) db/skiplist_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/version_edit_test:db/version_edit_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) db/version_edit_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/version_set_test:db/version_set_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) db/version_set_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/write_batch_test:db/write_batch_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) $(CXXFLAGS) db/write_batch_test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + +$(STATIC_OUTDIR)/memenv_test:$(STATIC_OUTDIR)/helpers/memenv/memenv_test.o $(STATIC_OUTDIR)/libmemenv.a $(STATIC_OUTDIR)/libleveldb.a $(TESTHARNESS) + $(XCRUN) $(CXX) $(LDFLAGS) $(STATIC_OUTDIR)/helpers/memenv/memenv_test.o $(STATIC_OUTDIR)/libmemenv.a $(STATIC_OUTDIR)/libleveldb.a $(TESTHARNESS) -o $@ $(LIBS) + +$(SHARED_OUTDIR)/db_bench:$(SHARED_OUTDIR)/db/db_bench.o $(SHARED_LIBS) $(TESTUTIL) + $(XCRUN) $(CXX) $(LDFLAGS) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SHARED_OUTDIR)/db/db_bench.o $(TESTUTIL) $(SHARED_OUTDIR)/$(SHARED_LIB3) -o $@ $(LIBS) + +.PHONY: run-shared +run-shared: $(SHARED_OUTDIR)/db_bench + LD_LIBRARY_PATH=$(SHARED_OUTDIR) $(SHARED_OUTDIR)/db_bench + +$(SIMULATOR_OUTDIR)/%.o: %.cc + xcrun -sdk iphonesimulator $(CXX) $(CXXFLAGS) $(SIMULATOR_CFLAGS) -c $< -o $@ + +$(DEVICE_OUTDIR)/%.o: %.cc + xcrun -sdk iphoneos $(CXX) $(CXXFLAGS) $(DEVICE_CFLAGS) -c $< -o $@ + +$(SIMULATOR_OUTDIR)/%.o: %.c + xcrun -sdk iphonesimulator $(CC) $(CFLAGS) $(SIMULATOR_CFLAGS) -c $< -o $@ + +$(DEVICE_OUTDIR)/%.o: %.c + xcrun -sdk iphoneos $(CC) $(CFLAGS) $(DEVICE_CFLAGS) -c $< -o $@ + +$(STATIC_OUTDIR)/%.o: %.cc $(CXX) $(CXXFLAGS) -c $< -o $@ -.c.o: +$(STATIC_OUTDIR)/%.o: %.c $(CC) $(CFLAGS) -c $< -o $@ -endif + +$(SHARED_OUTDIR)/%.o: %.cc + $(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@ + +$(SHARED_OUTDIR)/%.o: %.c + $(CC) $(CFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@ + +$(STATIC_OUTDIR)/port/port_posix_sse.o: port/port_posix_sse.cc + $(CXX) $(CXXFLAGS) $(PLATFORM_SSEFLAGS) -c $< -o $@ + +$(SHARED_OUTDIR)/port/port_posix_sse.o: port/port_posix_sse.cc + $(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(PLATFORM_SSEFLAGS) -c $< -o $@ diff --git a/src/leveldb/README b/src/leveldb/README deleted file mode 100644 index 3618adee..00000000 --- a/src/leveldb/README +++ /dev/null @@ -1,51 +0,0 @@ -leveldb: A key-value store -Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) - -The code under this directory implements a system for maintaining a -persistent key/value store. - -See doc/index.html for more explanation. -See doc/impl.html for a brief overview of the implementation. - -The public interface is in include/*.h. Callers should not include or -rely on the details of any other header files in this package. Those -internal APIs may be changed without warning. - -Guide to header files: - -include/db.h - Main interface to the DB: Start here - -include/options.h - Control over the behavior of an entire database, and also - control over the behavior of individual reads and writes. - -include/comparator.h - Abstraction for user-specified comparison function. If you want - just bytewise comparison of keys, you can use the default comparator, - but clients can write their own comparator implementations if they - want custom ordering (e.g. to handle different character - encodings, etc.) - -include/iterator.h - Interface for iterating over data. You can get an iterator - from a DB object. - -include/write_batch.h - Interface for atomically applying multiple updates to a database. - -include/slice.h - A simple module for maintaining a pointer and a length into some - other byte array. - -include/status.h - Status is returned from many of the public interfaces and is used - to report success and various kinds of errors. - -include/env.h - Abstraction of the OS environment. A posix implementation of - this interface is in util/env_posix.cc - -include/table.h -include/table_builder.h - Lower-level modules that most clients probably won't use directly diff --git a/src/leveldb/README.md b/src/leveldb/README.md index 480affb5..a010c508 100644 --- a/src/leveldb/README.md +++ b/src/leveldb/README.md @@ -1,5 +1,7 @@ **LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.** +[![Build Status](https://travis-ci.org/google/leveldb.svg?branch=master)](https://travis-ci.org/google/leveldb) + Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) # Features @@ -10,9 +12,11 @@ Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) * Multiple changes can be made in one atomic batch. * Users can create a transient snapshot to get a consistent view of data. * Forward and backward iteration is supported over the data. - * Data is automatically compressed using the [Snappy compression library](http://code.google.com/p/snappy). + * Data is automatically compressed using the [Snappy compression library](http://google.github.io/snappy/). * External activity (file system operations etc.) is relayed through a virtual interface so users can customize the operating system interactions. - * [Detailed documentation](http://htmlpreview.github.io/?https://github.com/google/leveldb/blob/master/doc/index.html) about how to use the library is included with the source code. + +# Documentation + [LevelDB library documentation](https://github.com/google/leveldb/blob/master/doc/index.md) is online and bundled with the source code. # Limitations @@ -20,6 +24,37 @@ Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) * Only a single process (possibly multi-threaded) can access a particular database at a time. * There is no client-server support builtin to the library. An application that needs such support will have to wrap their own server around the library. +# Contributing to the leveldb Project +The leveldb project welcomes contributions. leveldb's primary goal is to be +a reliable and fast key/value store. Changes that are in line with the +features/limitations outlined above, and meet the requirements below, +will be considered. + +Contribution requirements: + +1. **POSIX only**. We _generally_ will only accept changes that are both + compiled, and tested on a POSIX platform - usually Linux. Very small + changes will sometimes be accepted, but consider that more of an + exception than the rule. + +2. **Stable API**. We strive very hard to maintain a stable API. Changes that + require changes for projects using leveldb _might_ be rejected without + sufficient benefit to the project. + +3. **Tests**: All changes must be accompanied by a new (or changed) test, or + a sufficient explanation as to why a new (or changed) test is not required. + +## Submitting a Pull Request +Before any pull request will be accepted the author must first sign a +Contributor License Agreement (CLA) at https://cla.developers.google.com/. + +In order to keep the commit timeline linear +[squash](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Squashing-Commits) +your changes down to a single commit and [rebase](https://git-scm.com/docs/git-rebase) +on google/leveldb/master. This keeps the commit timeline linear and more easily sync'ed +with the internal repository at Google. More information at GitHub's +[About Git rebase](https://help.github.com/articles/about-git-rebase/) page. + # Performance Here is a performance report (with explanations) from the run of the @@ -78,29 +113,30 @@ by the one or two disk seeks needed to fetch the data from disk. Write performance will be mostly unaffected by whether or not the working set fits in memory. - readrandom : 16.677 micros/op; (approximately 60,000 reads per second) - readseq : 0.476 micros/op; 232.3 MB/s - readreverse : 0.724 micros/op; 152.9 MB/s + readrandom : 16.677 micros/op; (approximately 60,000 reads per second) + readseq : 0.476 micros/op; 232.3 MB/s + readreverse : 0.724 micros/op; 152.9 MB/s LevelDB compacts its underlying storage data in the background to improve read performance. The results listed above were done immediately after a lot of random writes. The results after compactions (which are usually triggered automatically) are better. - readrandom : 11.602 micros/op; (approximately 85,000 reads per second) - readseq : 0.423 micros/op; 261.8 MB/s - readreverse : 0.663 micros/op; 166.9 MB/s + readrandom : 11.602 micros/op; (approximately 85,000 reads per second) + readseq : 0.423 micros/op; 261.8 MB/s + readreverse : 0.663 micros/op; 166.9 MB/s Some of the high cost of reads comes from repeated decompression of blocks read from disk. If we supply enough cache to the leveldb so it can hold the uncompressed blocks in memory, the read performance improves again: - readrandom : 9.775 micros/op; (approximately 100,000 reads per second before compaction) - readrandom : 5.215 micros/op; (approximately 190,000 reads per second after compaction) + readrandom : 9.775 micros/op; (approximately 100,000 reads per second before compaction) + readrandom : 5.215 micros/op; (approximately 190,000 reads per second after compaction) ## Repository contents -See doc/index.html for more explanation. See doc/impl.html for a brief overview of the implementation. +See [doc/index.md](doc/index.md) for more explanation. See +[doc/impl.md](doc/impl.md) for a brief overview of the implementation. The public interface is in include/*.h. Callers should not include or rely on the details of any other header files in this package. Those @@ -113,7 +149,7 @@ Guide to header files: * **include/options.h**: Control over the behavior of an entire database, and also control over the behavior of individual reads and writes. -* **include/comparator.h**: Abstraction for user-specified comparison function. +* **include/comparator.h**: Abstraction for user-specified comparison function. If you want just bytewise comparison of keys, you can use the default comparator, but clients can write their own comparator implementations if they want custom ordering (e.g. to handle different character encodings, etc.) @@ -130,7 +166,7 @@ length into some other byte array. * **include/status.h**: Status is returned from many of the public interfaces and is used to report success and various kinds of errors. -* **include/env.h**: +* **include/env.h**: Abstraction of the OS environment. A posix implementation of this interface is in util/env_posix.cc diff --git a/src/leveldb/build_detect_platform b/src/leveldb/build_detect_platform index a1101c1b..929562dd 100755 --- a/src/leveldb/build_detect_platform +++ b/src/leveldb/build_detect_platform @@ -63,6 +63,7 @@ PLATFORM_SHARED_EXT="so" PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl," PLATFORM_SHARED_CFLAGS="-fPIC" PLATFORM_SHARED_VERSIONED=true +PLATFORM_SSEFLAGS= MEMCMP_FLAG= if [ "$CXX" = "g++" ]; then @@ -77,6 +78,7 @@ case "$TARGET_OS" in COMMON_FLAGS="$MEMCMP_FLAG -lpthread -DOS_LINUX -DCYGWIN" PLATFORM_LDFLAGS="-lpthread" PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc ;; Darwin) PLATFORM=OS_MACOSX @@ -85,24 +87,28 @@ case "$TARGET_OS" in [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name $INSTALL_PATH/" PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc ;; Linux) PLATFORM=OS_LINUX COMMON_FLAGS="$MEMCMP_FLAG -pthread -DOS_LINUX" PLATFORM_LDFLAGS="-pthread" PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc ;; SunOS) PLATFORM=OS_SOLARIS COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_SOLARIS" PLATFORM_LIBS="-lpthread -lrt" PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc ;; FreeBSD) PLATFORM=OS_FREEBSD COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_FREEBSD" PLATFORM_LIBS="-lpthread" PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc ;; GNU/kFreeBSD) PLATFORM=OS_KFREEBSD @@ -115,24 +121,28 @@ case "$TARGET_OS" in COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_NETBSD" PLATFORM_LIBS="-lpthread -lgcc_s" PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc ;; OpenBSD) PLATFORM=OS_OPENBSD COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_OPENBSD" PLATFORM_LDFLAGS="-pthread" PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc ;; DragonFly) PLATFORM=OS_DRAGONFLYBSD COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_DRAGONFLYBSD" PLATFORM_LIBS="-lpthread" PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc ;; OS_ANDROID_CROSSCOMPILE) PLATFORM=OS_ANDROID COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX" PLATFORM_LDFLAGS="" # All pthread features are in the Android C library PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc CROSS_COMPILE=true ;; HP-UX) @@ -140,6 +150,7 @@ case "$TARGET_OS" in COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_HPUX" PLATFORM_LDFLAGS="-pthread" PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc # man ld: +h internal_name PLATFORM_SHARED_LDFLAGS="-shared -Wl,+h -Wl," ;; @@ -148,6 +159,7 @@ case "$TARGET_OS" in COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX" [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` PORT_FILE=port/port_posix.cc + PORT_SSE_FILE=port/port_posix_sse.cc PLATFORM_SHARED_EXT= PLATFORM_SHARED_LDFLAGS= PLATFORM_SHARED_CFLAGS= @@ -175,14 +187,14 @@ DIRS="$PREFIX/db $PREFIX/util $PREFIX/table" set -f # temporarily disable globbing so that our patterns aren't expanded PRUNE_TEST="-name *test*.cc -prune" PRUNE_BENCH="-name *_bench.cc -prune" -PRUNE_TOOL="-name leveldb_main.cc -prune" +PRUNE_TOOL="-name leveldbutil.cc -prune" PORTABLE_FILES=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o $PRUNE_TOOL -o -name '*.cc' -print | sort | sed "s,^$PREFIX/,," | tr "\n" " "` set +f # re-enable globbing # The sources consist of the portable files, plus the platform-specific port # file. -echo "SOURCES=$PORTABLE_FILES $PORT_FILE" >> $OUTPUT +echo "SOURCES=$PORTABLE_FILES $PORT_FILE $PORT_SSE_FILE" >> $OUTPUT echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT if [ "$CROSS_COMPILE" = "true" ]; then @@ -198,7 +210,7 @@ else int main() {} EOF if [ "$?" = 0 ]; then - COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX -DLEVELDB_ATOMIC_PRESENT" + COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX -DLEVELDB_ATOMIC_PRESENT" PLATFORM_CXXFLAGS="-std=c++0x" else COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX" @@ -213,6 +225,21 @@ EOF fi rm -f $CXXOUTPUT 2>/dev/null + + # Test if gcc SSE 4.2 is supported + $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT -msse4.2 2>/dev/null </dev/null +fi + +# Use the SSE 4.2 CRC32C intrinsics iff runtime checks indicate compiler supports them. +if [ -n "$PLATFORM_SSEFLAGS" ]; then + PLATFORM_SSEFLAGS="$PLATFORM_SSEFLAGS -DLEVELDB_PLATFORM_POSIX_SSE" fi PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS" @@ -225,6 +252,7 @@ echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> $OUTPUT echo "PLATFORM_LIBS=$PLATFORM_LIBS" >> $OUTPUT echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> $OUTPUT echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> $OUTPUT +echo "PLATFORM_SSEFLAGS=$PLATFORM_SSEFLAGS" >> $OUTPUT echo "PLATFORM_SHARED_CFLAGS=$PLATFORM_SHARED_CFLAGS" >> $OUTPUT echo "PLATFORM_SHARED_EXT=$PLATFORM_SHARED_EXT" >> $OUTPUT echo "PLATFORM_SHARED_LDFLAGS=$PLATFORM_SHARED_LDFLAGS" >> $OUTPUT diff --git a/src/leveldb/db/c.cc b/src/leveldb/db/c.cc index 08ff0ad9..b23e3dcc 100644 --- a/src/leveldb/db/c.cc +++ b/src/leveldb/db/c.cc @@ -5,7 +5,9 @@ #include "leveldb/c.h" #include +#ifndef WIN32 #include +#endif #include "leveldb/cache.h" #include "leveldb/comparator.h" #include "leveldb/db.h" diff --git a/src/leveldb/db/corruption_test.cc b/src/leveldb/db/corruption_test.cc index 96afc689..37a484d2 100644 --- a/src/leveldb/db/corruption_test.cc +++ b/src/leveldb/db/corruption_test.cc @@ -36,7 +36,7 @@ class CorruptionTest { tiny_cache_ = NewLRUCache(100); options_.env = &env_; options_.block_cache = tiny_cache_; - dbname_ = test::TmpDir() + "/db_test"; + dbname_ = test::TmpDir() + "/corruption_test"; DestroyDB(dbname_, options_); db_ = NULL; diff --git a/src/leveldb/db/db_bench.cc b/src/leveldb/db/db_bench.cc index 705a170a..9ac04c41 100644 --- a/src/leveldb/db/db_bench.cc +++ b/src/leveldb/db/db_bench.cc @@ -33,6 +33,7 @@ // readmissing -- read N missing keys in random order // readhot -- read N times in random order from 1% section of DB // seekrandom -- N random seeks +// open -- cost of opening a DB // crc32c -- repeated crc32c of 4K of data // acquireload -- load N*1000 times // Meta operations: @@ -83,6 +84,14 @@ static bool FLAGS_histogram = false; // (initialized to default value by "main") static int FLAGS_write_buffer_size = 0; +// Number of bytes written to each file. +// (initialized to default value by "main") +static int FLAGS_max_file_size = 0; + +// Approximate size of user data packed per block (before compression). +// (initialized to default value by "main") +static int FLAGS_block_size = 0; + // Number of bytes to use as a cache of uncompressed data. // Negative means use default settings. static int FLAGS_cache_size = -1; @@ -99,12 +108,16 @@ static int FLAGS_bloom_bits = -1; // benchmark will fail. static bool FLAGS_use_existing_db = false; +// If true, reuse existing log/MANIFEST files when re-opening a database. +static bool FLAGS_reuse_logs = false; + // Use the db with the following name. static const char* FLAGS_db = NULL; namespace leveldb { namespace { +leveldb::Env* g_env = NULL; // Helper for quickly generating random data. class RandomGenerator { @@ -138,6 +151,7 @@ class RandomGenerator { } }; +#if defined(__linux) static Slice TrimSpace(Slice s) { size_t start = 0; while (start < s.size() && isspace(s[start])) { @@ -149,6 +163,7 @@ static Slice TrimSpace(Slice s) { } return Slice(s.data() + start, limit - start); } +#endif static void AppendWithSpace(std::string* str, Slice msg) { if (msg.empty()) return; @@ -180,7 +195,7 @@ class Stats { done_ = 0; bytes_ = 0; seconds_ = 0; - start_ = Env::Default()->NowMicros(); + start_ = g_env->NowMicros(); finish_ = start_; message_.clear(); } @@ -198,7 +213,7 @@ class Stats { } void Stop() { - finish_ = Env::Default()->NowMicros(); + finish_ = g_env->NowMicros(); seconds_ = (finish_ - start_) * 1e-6; } @@ -208,7 +223,7 @@ class Stats { void FinishedSingleOp() { if (FLAGS_histogram) { - double now = Env::Default()->NowMicros(); + double now = g_env->NowMicros(); double micros = now - last_op_finish_; hist_.Add(micros); if (micros > 20000) { @@ -398,10 +413,10 @@ class Benchmark { reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), heap_counter_(0) { std::vector files; - Env::Default()->GetChildren(FLAGS_db, &files); + g_env->GetChildren(FLAGS_db, &files); for (size_t i = 0; i < files.size(); i++) { if (Slice(files[i]).starts_with("heap-")) { - Env::Default()->DeleteFile(std::string(FLAGS_db) + "/" + files[i]); + g_env->DeleteFile(std::string(FLAGS_db) + "/" + files[i]); } } if (!FLAGS_use_existing_db) { @@ -442,7 +457,11 @@ class Benchmark { bool fresh_db = false; int num_threads = FLAGS_threads; - if (name == Slice("fillseq")) { + if (name == Slice("open")) { + method = &Benchmark::OpenBench; + num_ /= 10000; + if (num_ < 1) num_ = 1; + } else if (name == Slice("fillseq")) { fresh_db = true; method = &Benchmark::WriteSeq; } else if (name == Slice("fillbatch")) { @@ -579,7 +598,7 @@ class Benchmark { arg[i].shared = &shared; arg[i].thread = new ThreadState(i); arg[i].thread->shared = &shared; - Env::Default()->StartThread(ThreadBody, &arg[i]); + g_env->StartThread(ThreadBody, &arg[i]); } shared.mu.Lock(); @@ -690,11 +709,15 @@ class Benchmark { void Open() { assert(db_ == NULL); Options options; + options.env = g_env; options.create_if_missing = !FLAGS_use_existing_db; options.block_cache = cache_; options.write_buffer_size = FLAGS_write_buffer_size; + options.max_file_size = FLAGS_max_file_size; + options.block_size = FLAGS_block_size; options.max_open_files = FLAGS_open_files; options.filter_policy = filter_policy_; + options.reuse_logs = FLAGS_reuse_logs; Status s = DB::Open(options, FLAGS_db, &db_); if (!s.ok()) { fprintf(stderr, "open error: %s\n", s.ToString().c_str()); @@ -702,6 +725,14 @@ class Benchmark { } } + void OpenBench(ThreadState* thread) { + for (int i = 0; i < num_; i++) { + delete db_; + Open(); + thread->stats.FinishedSingleOp(); + } + } + void WriteSeq(ThreadState* thread) { DoWrite(thread, true); } @@ -906,7 +937,7 @@ class Benchmark { char fname[100]; snprintf(fname, sizeof(fname), "%s/heap-%04d", FLAGS_db, ++heap_counter_); WritableFile* file; - Status s = Env::Default()->NewWritableFile(fname, &file); + Status s = g_env->NewWritableFile(fname, &file); if (!s.ok()) { fprintf(stderr, "%s\n", s.ToString().c_str()); return; @@ -915,7 +946,7 @@ class Benchmark { delete file; if (!ok) { fprintf(stderr, "heap profiling not supported\n"); - Env::Default()->DeleteFile(fname); + g_env->DeleteFile(fname); } } }; @@ -924,6 +955,8 @@ class Benchmark { int main(int argc, char** argv) { FLAGS_write_buffer_size = leveldb::Options().write_buffer_size; + FLAGS_max_file_size = leveldb::Options().max_file_size; + FLAGS_block_size = leveldb::Options().block_size; FLAGS_open_files = leveldb::Options().max_open_files; std::string default_db_path; @@ -941,6 +974,9 @@ int main(int argc, char** argv) { } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 && (n == 0 || n == 1)) { FLAGS_use_existing_db = n; + } else if (sscanf(argv[i], "--reuse_logs=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_reuse_logs = n; } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { FLAGS_num = n; } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { @@ -951,6 +987,10 @@ int main(int argc, char** argv) { FLAGS_value_size = n; } else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) { FLAGS_write_buffer_size = n; + } else if (sscanf(argv[i], "--max_file_size=%d%c", &n, &junk) == 1) { + FLAGS_max_file_size = n; + } else if (sscanf(argv[i], "--block_size=%d%c", &n, &junk) == 1) { + FLAGS_block_size = n; } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) { FLAGS_cache_size = n; } else if (sscanf(argv[i], "--bloom_bits=%d%c", &n, &junk) == 1) { @@ -965,9 +1005,11 @@ int main(int argc, char** argv) { } } + leveldb::g_env = leveldb::Env::Default(); + // Choose a location for the test database if none given with --db= if (FLAGS_db == NULL) { - leveldb::Env::Default()->GetTestDirectory(&default_db_path); + leveldb::g_env->GetTestDirectory(&default_db_path); default_db_path += "/dbbench"; FLAGS_db = default_db_path.c_str(); } diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc index 31e9522f..3bb58e56 100644 --- a/src/leveldb/db/db_impl.cc +++ b/src/leveldb/db/db_impl.cc @@ -96,6 +96,7 @@ Options SanitizeOptions(const std::string& dbname, result.filter_policy = (src.filter_policy != NULL) ? ipolicy : NULL; ClipToRange(&result.max_open_files, 64 + kNumNonTableCacheFiles, 50000); ClipToRange(&result.write_buffer_size, 64<<10, 1<<30); + ClipToRange(&result.max_file_size, 1<<20, 1<<30); ClipToRange(&result.block_size, 1<<10, 4<<20); if (result.info_log == NULL) { // Open a log file in the same directory as the db @@ -125,7 +126,7 @@ DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) db_lock_(NULL), shutting_down_(NULL), bg_cv_(&mutex_), - mem_(new MemTable(internal_comparator_)), + mem_(NULL), imm_(NULL), logfile_(NULL), logfile_number_(0), @@ -134,7 +135,6 @@ DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) tmp_batch_(new WriteBatch), bg_compaction_scheduled_(false), manual_compaction_(NULL) { - mem_->Ref(); has_imm_.Release_Store(NULL); // Reserve ten files or so for other uses and give the rest to TableCache. @@ -271,7 +271,7 @@ void DBImpl::DeleteObsoleteFiles() { } } -Status DBImpl::Recover(VersionEdit* edit) { +Status DBImpl::Recover(VersionEdit* edit, bool *save_manifest) { mutex_.AssertHeld(); // Ignore error from CreateDir since the creation of the DB is @@ -301,66 +301,69 @@ Status DBImpl::Recover(VersionEdit* edit) { } } - s = versions_->Recover(); - if (s.ok()) { - SequenceNumber max_sequence(0); - - // Recover from all newer log files than the ones named in the - // descriptor (new log files may have been added by the previous - // incarnation without registering them in the descriptor). - // - // Note that PrevLogNumber() is no longer used, but we pay - // attention to it in case we are recovering a database - // produced by an older version of leveldb. - const uint64_t min_log = versions_->LogNumber(); - const uint64_t prev_log = versions_->PrevLogNumber(); - std::vector filenames; - s = env_->GetChildren(dbname_, &filenames); + s = versions_->Recover(save_manifest); + if (!s.ok()) { + return s; + } + SequenceNumber max_sequence(0); + + // Recover from all newer log files than the ones named in the + // descriptor (new log files may have been added by the previous + // incarnation without registering them in the descriptor). + // + // Note that PrevLogNumber() is no longer used, but we pay + // attention to it in case we are recovering a database + // produced by an older version of leveldb. + const uint64_t min_log = versions_->LogNumber(); + const uint64_t prev_log = versions_->PrevLogNumber(); + std::vector filenames; + s = env_->GetChildren(dbname_, &filenames); + if (!s.ok()) { + return s; + } + std::set expected; + versions_->AddLiveFiles(&expected); + uint64_t number; + FileType type; + std::vector logs; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + expected.erase(number); + if (type == kLogFile && ((number >= min_log) || (number == prev_log))) + logs.push_back(number); + } + } + if (!expected.empty()) { + char buf[50]; + snprintf(buf, sizeof(buf), "%d missing files; e.g.", + static_cast(expected.size())); + return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin()))); + } + + // Recover in the order in which the logs were generated + std::sort(logs.begin(), logs.end()); + for (size_t i = 0; i < logs.size(); i++) { + s = RecoverLogFile(logs[i], (i == logs.size() - 1), save_manifest, edit, + &max_sequence); if (!s.ok()) { return s; } - std::set expected; - versions_->AddLiveFiles(&expected); - uint64_t number; - FileType type; - std::vector logs; - for (size_t i = 0; i < filenames.size(); i++) { - if (ParseFileName(filenames[i], &number, &type)) { - expected.erase(number); - if (type == kLogFile && ((number >= min_log) || (number == prev_log))) - logs.push_back(number); - } - } - if (!expected.empty()) { - char buf[50]; - snprintf(buf, sizeof(buf), "%d missing files; e.g.", - static_cast(expected.size())); - return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin()))); - } - - // Recover in the order in which the logs were generated - std::sort(logs.begin(), logs.end()); - for (size_t i = 0; i < logs.size(); i++) { - s = RecoverLogFile(logs[i], edit, &max_sequence); - // The previous incarnation may not have written any MANIFEST - // records after allocating this log number. So we manually - // update the file number allocation counter in VersionSet. - versions_->MarkFileNumberUsed(logs[i]); - } + // The previous incarnation may not have written any MANIFEST + // records after allocating this log number. So we manually + // update the file number allocation counter in VersionSet. + versions_->MarkFileNumberUsed(logs[i]); + } - if (s.ok()) { - if (versions_->LastSequence() < max_sequence) { - versions_->SetLastSequence(max_sequence); - } - } + if (versions_->LastSequence() < max_sequence) { + versions_->SetLastSequence(max_sequence); } - return s; + return Status::OK(); } -Status DBImpl::RecoverLogFile(uint64_t log_number, - VersionEdit* edit, +Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, + bool* save_manifest, VersionEdit* edit, SequenceNumber* max_sequence) { struct LogReporter : public log::Reader::Reporter { Env* env; @@ -405,12 +408,13 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, std::string scratch; Slice record; WriteBatch batch; + int compactions = 0; MemTable* mem = NULL; while (reader.ReadRecord(&record, &scratch) && status.ok()) { if (record.size() < 12) { reporter.Corruption( - record.size(), Status::Corruption("log record too small")); + record.size(), Status::Corruption("log record too small", fname)); continue; } WriteBatchInternal::SetContents(&batch, record); @@ -432,25 +436,51 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, } if (mem->ApproximateMemoryUsage() > options_.write_buffer_size) { + compactions++; + *save_manifest = true; status = WriteLevel0Table(mem, edit, NULL); + mem->Unref(); + mem = NULL; if (!status.ok()) { // Reflect errors immediately so that conditions like full // file-systems cause the DB::Open() to fail. break; } - mem->Unref(); - mem = NULL; } } - if (status.ok() && mem != NULL) { - status = WriteLevel0Table(mem, edit, NULL); - // Reflect errors immediately so that conditions like full - // file-systems cause the DB::Open() to fail. + delete file; + + // See if we should keep reusing the last log file. + if (status.ok() && options_.reuse_logs && last_log && compactions == 0) { + assert(logfile_ == NULL); + assert(log_ == NULL); + assert(mem_ == NULL); + uint64_t lfile_size; + if (env_->GetFileSize(fname, &lfile_size).ok() && + env_->NewAppendableFile(fname, &logfile_).ok()) { + Log(options_.info_log, "Reusing old log %s \n", fname.c_str()); + log_ = new log::Writer(logfile_, lfile_size); + logfile_number_ = log_number; + if (mem != NULL) { + mem_ = mem; + mem = NULL; + } else { + // mem can be NULL if lognum exists but was empty. + mem_ = new MemTable(internal_comparator_); + mem_->Ref(); + } + } } - if (mem != NULL) mem->Unref(); - delete file; + if (mem != NULL) { + // mem did not get reused; compact it. + if (status.ok()) { + *save_manifest = true; + status = WriteLevel0Table(mem, edit, NULL); + } + mem->Unref(); + } return status; } @@ -822,8 +852,9 @@ Status DBImpl::FinishCompactionOutputFile(CompactionState* compact, delete iter; if (s.ok()) { Log(options_.info_log, - "Generated table #%llu: %lld keys, %lld bytes", + "Generated table #%llu@%d: %lld keys, %lld bytes", (unsigned long long) output_number, + compact->compaction->level(), (unsigned long long) current_entries, (unsigned long long) current_bytes); } @@ -1396,6 +1427,19 @@ bool DBImpl::GetProperty(const Slice& property, std::string* value) { } else if (in == "sstables") { *value = versions_->current()->DebugString(); return true; + } else if (in == "approximate-memory-usage") { + size_t total_usage = options_.block_cache->TotalCharge(); + if (mem_) { + total_usage += mem_->ApproximateMemoryUsage(); + } + if (imm_) { + total_usage += imm_->ApproximateMemoryUsage(); + } + char buf[50]; + snprintf(buf, sizeof(buf), "%llu", + static_cast(total_usage)); + value->append(buf); + return true; } return false; @@ -1450,8 +1494,11 @@ Status DB::Open(const Options& options, const std::string& dbname, DBImpl* impl = new DBImpl(options, dbname); impl->mutex_.Lock(); VersionEdit edit; - Status s = impl->Recover(&edit); // Handles create_if_missing, error_if_exists - if (s.ok()) { + // Recover handles create_if_missing, error_if_exists + bool save_manifest = false; + Status s = impl->Recover(&edit, &save_manifest); + if (s.ok() && impl->mem_ == NULL) { + // Create new log and a corresponding memtable. uint64_t new_log_number = impl->versions_->NewFileNumber(); WritableFile* lfile; s = options.env->NewWritableFile(LogFileName(dbname, new_log_number), @@ -1461,16 +1508,22 @@ Status DB::Open(const Options& options, const std::string& dbname, impl->logfile_ = lfile; impl->logfile_number_ = new_log_number; impl->log_ = new log::Writer(lfile); - s = impl->versions_->LogAndApply(&edit, &impl->mutex_); - } - - if (s.ok()) { - impl->DeleteObsoleteFiles(); - impl->MaybeScheduleCompaction(); + impl->mem_ = new MemTable(impl->internal_comparator_); + impl->mem_->Ref(); } } + if (s.ok() && save_manifest) { + edit.SetPrevLogNumber(0); // No older logs needed after recovery. + edit.SetLogNumber(impl->logfile_number_); + s = impl->versions_->LogAndApply(&edit, &impl->mutex_); + } + if (s.ok()) { + impl->DeleteObsoleteFiles(); + impl->MaybeScheduleCompaction(); + } impl->mutex_.Unlock(); if (s.ok()) { + assert(impl->mem_ != NULL); *dbptr = impl; } else { delete impl; diff --git a/src/leveldb/db/db_impl.h b/src/leveldb/db/db_impl.h index cfc99816..8ff323e7 100644 --- a/src/leveldb/db/db_impl.h +++ b/src/leveldb/db/db_impl.h @@ -78,7 +78,8 @@ class DBImpl : public DB { // Recover the descriptor from persistent storage. May do a significant // amount of work to recover recently logged updates. Any changes to // be made to the descriptor are added to *edit. - Status Recover(VersionEdit* edit) EXCLUSIVE_LOCKS_REQUIRED(mutex_); + Status Recover(VersionEdit* edit, bool* save_manifest) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); void MaybeIgnoreError(Status* s) const; @@ -90,9 +91,8 @@ class DBImpl : public DB { // Errors are recorded in bg_error_. void CompactMemTable() EXCLUSIVE_LOCKS_REQUIRED(mutex_); - Status RecoverLogFile(uint64_t log_number, - VersionEdit* edit, - SequenceNumber* max_sequence) + Status RecoverLogFile(uint64_t log_number, bool last_log, bool* save_manifest, + VersionEdit* edit, SequenceNumber* max_sequence) EXCLUSIVE_LOCKS_REQUIRED(mutex_); Status WriteLevel0Table(MemTable* mem, VersionEdit* edit, Version* base) diff --git a/src/leveldb/db/db_test.cc b/src/leveldb/db/db_test.cc index 0fed9137..a0b08bc1 100644 --- a/src/leveldb/db/db_test.cc +++ b/src/leveldb/db/db_test.cc @@ -193,6 +193,7 @@ class DBTest { // Sequence of option configurations to try enum OptionConfig { kDefault, + kReuse, kFilter, kUncompressed, kEnd @@ -237,7 +238,11 @@ class DBTest { // Return the current option configuration. Options CurrentOptions() { Options options; + options.reuse_logs = false; switch (option_config_) { + case kReuse: + options.reuse_logs = true; + break; case kFilter: options.filter_policy = filter_policy_; break; @@ -558,6 +563,17 @@ TEST(DBTest, GetFromVersions) { } while (ChangeOptions()); } +TEST(DBTest, GetMemUsage) { + do { + ASSERT_OK(Put("foo", "v1")); + std::string val; + ASSERT_TRUE(db_->GetProperty("leveldb.approximate-memory-usage", &val)); + int mem_usage = atoi(val.c_str()); + ASSERT_GT(mem_usage, 0); + ASSERT_LT(mem_usage, 5*1024*1024); + } while (ChangeOptions()); +} + TEST(DBTest, GetSnapshot) { do { // Try with both a short key and a long key @@ -1080,6 +1096,14 @@ TEST(DBTest, ApproximateSizes) { // 0 because GetApproximateSizes() does not account for memtable space ASSERT_TRUE(Between(Size("", Key(50)), 0, 0)); + if (options.reuse_logs) { + // Recovery will reuse memtable, and GetApproximateSizes() does not + // account for memtable usage; + Reopen(&options); + ASSERT_TRUE(Between(Size("", Key(50)), 0, 0)); + continue; + } + // Check sizes across recovery by reopening a few times for (int run = 0; run < 3; run++) { Reopen(&options); @@ -1123,6 +1147,11 @@ TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) { ASSERT_OK(Put(Key(6), RandomString(&rnd, 300000))); ASSERT_OK(Put(Key(7), RandomString(&rnd, 10000))); + if (options.reuse_logs) { + // Need to force a memtable compaction since recovery does not do so. + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + } + // Check sizes across recovery by reopening a few times for (int run = 0; run < 3; run++) { Reopen(&options); @@ -2084,7 +2113,8 @@ void BM_LogAndApply(int iters, int num_base_files) { InternalKeyComparator cmp(BytewiseComparator()); Options options; VersionSet vset(dbname, &options, NULL, &cmp); - ASSERT_OK(vset.Recover()); + bool save_manifest; + ASSERT_OK(vset.Recover(&save_manifest)); VersionEdit vbase; uint64_t fnum = 1; for (int i = 0; i < num_base_files; i++) { diff --git a/src/leveldb/db/fault_injection_test.cc b/src/leveldb/db/fault_injection_test.cc new file mode 100644 index 00000000..875dfe81 --- /dev/null +++ b/src/leveldb/db/fault_injection_test.cc @@ -0,0 +1,554 @@ +// Copyright 2014 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// This test uses a custom Env to keep track of the state of a filesystem as of +// the last "sync". It then checks for data loss errors by purposely dropping +// file data (or entire files) not protected by a "sync". + +#include "leveldb/db.h" + +#include +#include +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/log_format.h" +#include "db/version_set.h" +#include "leveldb/cache.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "leveldb/write_batch.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static const int kValueSize = 1000; +static const int kMaxNumValues = 2000; +static const size_t kNumIterations = 3; + +class FaultInjectionTestEnv; + +namespace { + +// Assume a filename, and not a directory name like "/foo/bar/" +static std::string GetDirName(const std::string filename) { + size_t found = filename.find_last_of("/\\"); + if (found == std::string::npos) { + return ""; + } else { + return filename.substr(0, found); + } +} + +Status SyncDir(const std::string& dir) { + // As this is a test it isn't required to *actually* sync this directory. + return Status::OK(); +} + +// A basic file truncation function suitable for this test. +Status Truncate(const std::string& filename, uint64_t length) { + leveldb::Env* env = leveldb::Env::Default(); + + SequentialFile* orig_file; + Status s = env->NewSequentialFile(filename, &orig_file); + if (!s.ok()) + return s; + + char* scratch = new char[length]; + leveldb::Slice result; + s = orig_file->Read(length, &result, scratch); + delete orig_file; + if (s.ok()) { + std::string tmp_name = GetDirName(filename) + "/truncate.tmp"; + WritableFile* tmp_file; + s = env->NewWritableFile(tmp_name, &tmp_file); + if (s.ok()) { + s = tmp_file->Append(result); + delete tmp_file; + if (s.ok()) { + s = env->RenameFile(tmp_name, filename); + } else { + env->DeleteFile(tmp_name); + } + } + } + + delete[] scratch; + + return s; +} + +struct FileState { + std::string filename_; + ssize_t pos_; + ssize_t pos_at_last_sync_; + ssize_t pos_at_last_flush_; + + FileState(const std::string& filename) + : filename_(filename), + pos_(-1), + pos_at_last_sync_(-1), + pos_at_last_flush_(-1) { } + + FileState() : pos_(-1), pos_at_last_sync_(-1), pos_at_last_flush_(-1) {} + + bool IsFullySynced() const { return pos_ <= 0 || pos_ == pos_at_last_sync_; } + + Status DropUnsyncedData() const; +}; + +} // anonymous namespace + +// A wrapper around WritableFile which informs another Env whenever this file +// is written to or sync'ed. +class TestWritableFile : public WritableFile { + public: + TestWritableFile(const FileState& state, + WritableFile* f, + FaultInjectionTestEnv* env); + virtual ~TestWritableFile(); + virtual Status Append(const Slice& data); + virtual Status Close(); + virtual Status Flush(); + virtual Status Sync(); + + private: + FileState state_; + WritableFile* target_; + bool writable_file_opened_; + FaultInjectionTestEnv* env_; + + Status SyncParent(); +}; + +class FaultInjectionTestEnv : public EnvWrapper { + public: + FaultInjectionTestEnv() : EnvWrapper(Env::Default()), filesystem_active_(true) {} + virtual ~FaultInjectionTestEnv() { } + virtual Status NewWritableFile(const std::string& fname, + WritableFile** result); + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result); + virtual Status DeleteFile(const std::string& f); + virtual Status RenameFile(const std::string& s, const std::string& t); + + void WritableFileClosed(const FileState& state); + Status DropUnsyncedFileData(); + Status DeleteFilesCreatedAfterLastDirSync(); + void DirWasSynced(); + bool IsFileCreatedSinceLastDirSync(const std::string& filename); + void ResetState(); + void UntrackFile(const std::string& f); + // Setting the filesystem to inactive is the test equivalent to simulating a + // system reset. Setting to inactive will freeze our saved filesystem state so + // that it will stop being recorded. It can then be reset back to the state at + // the time of the reset. + bool IsFilesystemActive() const { return filesystem_active_; } + void SetFilesystemActive(bool active) { filesystem_active_ = active; } + + private: + port::Mutex mutex_; + std::map db_file_state_; + std::set new_files_since_last_dir_sync_; + bool filesystem_active_; // Record flushes, syncs, writes +}; + +TestWritableFile::TestWritableFile(const FileState& state, + WritableFile* f, + FaultInjectionTestEnv* env) + : state_(state), + target_(f), + writable_file_opened_(true), + env_(env) { + assert(f != NULL); +} + +TestWritableFile::~TestWritableFile() { + if (writable_file_opened_) { + Close(); + } + delete target_; +} + +Status TestWritableFile::Append(const Slice& data) { + Status s = target_->Append(data); + if (s.ok() && env_->IsFilesystemActive()) { + state_.pos_ += data.size(); + } + return s; +} + +Status TestWritableFile::Close() { + writable_file_opened_ = false; + Status s = target_->Close(); + if (s.ok()) { + env_->WritableFileClosed(state_); + } + return s; +} + +Status TestWritableFile::Flush() { + Status s = target_->Flush(); + if (s.ok() && env_->IsFilesystemActive()) { + state_.pos_at_last_flush_ = state_.pos_; + } + return s; +} + +Status TestWritableFile::SyncParent() { + Status s = SyncDir(GetDirName(state_.filename_)); + if (s.ok()) { + env_->DirWasSynced(); + } + return s; +} + +Status TestWritableFile::Sync() { + if (!env_->IsFilesystemActive()) { + return Status::OK(); + } + // Ensure new files referred to by the manifest are in the filesystem. + Status s = target_->Sync(); + if (s.ok()) { + state_.pos_at_last_sync_ = state_.pos_; + } + if (env_->IsFileCreatedSinceLastDirSync(state_.filename_)) { + Status ps = SyncParent(); + if (s.ok() && !ps.ok()) { + s = ps; + } + } + return s; +} + +Status FaultInjectionTestEnv::NewWritableFile(const std::string& fname, + WritableFile** result) { + WritableFile* actual_writable_file; + Status s = target()->NewWritableFile(fname, &actual_writable_file); + if (s.ok()) { + FileState state(fname); + state.pos_ = 0; + *result = new TestWritableFile(state, actual_writable_file, this); + // NewWritableFile doesn't append to files, so if the same file is + // opened again then it will be truncated - so forget our saved + // state. + UntrackFile(fname); + MutexLock l(&mutex_); + new_files_since_last_dir_sync_.insert(fname); + } + return s; +} + +Status FaultInjectionTestEnv::NewAppendableFile(const std::string& fname, + WritableFile** result) { + WritableFile* actual_writable_file; + Status s = target()->NewAppendableFile(fname, &actual_writable_file); + if (s.ok()) { + FileState state(fname); + state.pos_ = 0; + { + MutexLock l(&mutex_); + if (db_file_state_.count(fname) == 0) { + new_files_since_last_dir_sync_.insert(fname); + } else { + state = db_file_state_[fname]; + } + } + *result = new TestWritableFile(state, actual_writable_file, this); + } + return s; +} + +Status FaultInjectionTestEnv::DropUnsyncedFileData() { + Status s; + MutexLock l(&mutex_); + for (std::map::const_iterator it = + db_file_state_.begin(); + s.ok() && it != db_file_state_.end(); ++it) { + const FileState& state = it->second; + if (!state.IsFullySynced()) { + s = state.DropUnsyncedData(); + } + } + return s; +} + +void FaultInjectionTestEnv::DirWasSynced() { + MutexLock l(&mutex_); + new_files_since_last_dir_sync_.clear(); +} + +bool FaultInjectionTestEnv::IsFileCreatedSinceLastDirSync( + const std::string& filename) { + MutexLock l(&mutex_); + return new_files_since_last_dir_sync_.find(filename) != + new_files_since_last_dir_sync_.end(); +} + +void FaultInjectionTestEnv::UntrackFile(const std::string& f) { + MutexLock l(&mutex_); + db_file_state_.erase(f); + new_files_since_last_dir_sync_.erase(f); +} + +Status FaultInjectionTestEnv::DeleteFile(const std::string& f) { + Status s = EnvWrapper::DeleteFile(f); + ASSERT_OK(s); + if (s.ok()) { + UntrackFile(f); + } + return s; +} + +Status FaultInjectionTestEnv::RenameFile(const std::string& s, + const std::string& t) { + Status ret = EnvWrapper::RenameFile(s, t); + + if (ret.ok()) { + MutexLock l(&mutex_); + if (db_file_state_.find(s) != db_file_state_.end()) { + db_file_state_[t] = db_file_state_[s]; + db_file_state_.erase(s); + } + + if (new_files_since_last_dir_sync_.erase(s) != 0) { + assert(new_files_since_last_dir_sync_.find(t) == + new_files_since_last_dir_sync_.end()); + new_files_since_last_dir_sync_.insert(t); + } + } + + return ret; +} + +void FaultInjectionTestEnv::ResetState() { + // Since we are not destroying the database, the existing files + // should keep their recorded synced/flushed state. Therefore + // we do not reset db_file_state_ and new_files_since_last_dir_sync_. + MutexLock l(&mutex_); + SetFilesystemActive(true); +} + +Status FaultInjectionTestEnv::DeleteFilesCreatedAfterLastDirSync() { + // Because DeleteFile access this container make a copy to avoid deadlock + mutex_.Lock(); + std::set new_files(new_files_since_last_dir_sync_.begin(), + new_files_since_last_dir_sync_.end()); + mutex_.Unlock(); + Status s; + std::set::const_iterator it; + for (it = new_files.begin(); s.ok() && it != new_files.end(); ++it) { + s = DeleteFile(*it); + } + return s; +} + +void FaultInjectionTestEnv::WritableFileClosed(const FileState& state) { + MutexLock l(&mutex_); + db_file_state_[state.filename_] = state; +} + +Status FileState::DropUnsyncedData() const { + ssize_t sync_pos = pos_at_last_sync_ == -1 ? 0 : pos_at_last_sync_; + return Truncate(filename_, sync_pos); +} + +class FaultInjectionTest { + public: + enum ExpectedVerifResult { VAL_EXPECT_NO_ERROR, VAL_EXPECT_ERROR }; + enum ResetMethod { RESET_DROP_UNSYNCED_DATA, RESET_DELETE_UNSYNCED_FILES }; + + FaultInjectionTestEnv* env_; + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; + + FaultInjectionTest() + : env_(new FaultInjectionTestEnv), + tiny_cache_(NewLRUCache(100)), + db_(NULL) { + dbname_ = test::TmpDir() + "/fault_test"; + DestroyDB(dbname_, Options()); // Destroy any db from earlier run + options_.reuse_logs = true; + options_.env = env_; + options_.paranoid_checks = true; + options_.block_cache = tiny_cache_; + options_.create_if_missing = true; + } + + ~FaultInjectionTest() { + CloseDB(); + DestroyDB(dbname_, Options()); + delete tiny_cache_; + delete env_; + } + + void ReuseLogs(bool reuse) { + options_.reuse_logs = reuse; + } + + void Build(int start_idx, int num_vals) { + std::string key_space, value_space; + WriteBatch batch; + for (int i = start_idx; i < start_idx + num_vals; i++) { + Slice key = Key(i, &key_space); + batch.Clear(); + batch.Put(key, Value(i, &value_space)); + WriteOptions options; + ASSERT_OK(db_->Write(options, &batch)); + } + } + + Status ReadValue(int i, std::string* val) const { + std::string key_space, value_space; + Slice key = Key(i, &key_space); + Value(i, &value_space); + ReadOptions options; + return db_->Get(options, key, val); + } + + Status Verify(int start_idx, int num_vals, + ExpectedVerifResult expected) const { + std::string val; + std::string value_space; + Status s; + for (int i = start_idx; i < start_idx + num_vals && s.ok(); i++) { + Value(i, &value_space); + s = ReadValue(i, &val); + if (expected == VAL_EXPECT_NO_ERROR) { + if (s.ok()) { + ASSERT_EQ(value_space, val); + } + } else if (s.ok()) { + fprintf(stderr, "Expected an error at %d, but was OK\n", i); + s = Status::IOError(dbname_, "Expected value error:"); + } else { + s = Status::OK(); // An expected error + } + } + return s; + } + + // Return the ith key + Slice Key(int i, std::string* storage) const { + char buf[100]; + snprintf(buf, sizeof(buf), "%016d", i); + storage->assign(buf, strlen(buf)); + return Slice(*storage); + } + + // Return the value to associate with the specified key + Slice Value(int k, std::string* storage) const { + Random r(k); + return test::RandomString(&r, kValueSize, storage); + } + + Status OpenDB() { + delete db_; + db_ = NULL; + env_->ResetState(); + return DB::Open(options_, dbname_, &db_); + } + + void CloseDB() { + delete db_; + db_ = NULL; + } + + void DeleteAllData() { + Iterator* iter = db_->NewIterator(ReadOptions()); + WriteOptions options; + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ASSERT_OK(db_->Delete(WriteOptions(), iter->key())); + } + + delete iter; + } + + void ResetDBState(ResetMethod reset_method) { + switch (reset_method) { + case RESET_DROP_UNSYNCED_DATA: + ASSERT_OK(env_->DropUnsyncedFileData()); + break; + case RESET_DELETE_UNSYNCED_FILES: + ASSERT_OK(env_->DeleteFilesCreatedAfterLastDirSync()); + break; + default: + assert(false); + } + } + + void PartialCompactTestPreFault(int num_pre_sync, int num_post_sync) { + DeleteAllData(); + Build(0, num_pre_sync); + db_->CompactRange(NULL, NULL); + Build(num_pre_sync, num_post_sync); + } + + void PartialCompactTestReopenWithFault(ResetMethod reset_method, + int num_pre_sync, + int num_post_sync) { + env_->SetFilesystemActive(false); + CloseDB(); + ResetDBState(reset_method); + ASSERT_OK(OpenDB()); + ASSERT_OK(Verify(0, num_pre_sync, FaultInjectionTest::VAL_EXPECT_NO_ERROR)); + ASSERT_OK(Verify(num_pre_sync, num_post_sync, FaultInjectionTest::VAL_EXPECT_ERROR)); + } + + void NoWriteTestPreFault() { + } + + void NoWriteTestReopenWithFault(ResetMethod reset_method) { + CloseDB(); + ResetDBState(reset_method); + ASSERT_OK(OpenDB()); + } + + void DoTest() { + Random rnd(0); + ASSERT_OK(OpenDB()); + for (size_t idx = 0; idx < kNumIterations; idx++) { + int num_pre_sync = rnd.Uniform(kMaxNumValues); + int num_post_sync = rnd.Uniform(kMaxNumValues); + + PartialCompactTestPreFault(num_pre_sync, num_post_sync); + PartialCompactTestReopenWithFault(RESET_DROP_UNSYNCED_DATA, + num_pre_sync, + num_post_sync); + + NoWriteTestPreFault(); + NoWriteTestReopenWithFault(RESET_DROP_UNSYNCED_DATA); + + PartialCompactTestPreFault(num_pre_sync, num_post_sync); + // No new files created so we expect all values since no files will be + // dropped. + PartialCompactTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES, + num_pre_sync + num_post_sync, + 0); + + NoWriteTestPreFault(); + NoWriteTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES); + } + } +}; + +TEST(FaultInjectionTest, FaultTestNoLogReuse) { + ReuseLogs(false); + DoTest(); +} + +TEST(FaultInjectionTest, FaultTestWithLogReuse) { + ReuseLogs(true); + DoTest(); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/leveldb_main.cc b/src/leveldb/db/leveldbutil.cc similarity index 96% rename from src/leveldb/db/leveldb_main.cc rename to src/leveldb/db/leveldbutil.cc index 9f4b7dd7..d06d64d6 100644 --- a/src/leveldb/db/leveldb_main.cc +++ b/src/leveldb/db/leveldbutil.cc @@ -19,6 +19,7 @@ class StdoutPrinter : public WritableFile { virtual Status Close() { return Status::OK(); } virtual Status Flush() { return Status::OK(); } virtual Status Sync() { return Status::OK(); } + virtual std::string GetName() const { return "[stdout]"; } }; bool HandleDumpCommand(Env* env, char** files, int num) { diff --git a/src/leveldb/db/log_format.h b/src/leveldb/db/log_format.h index a8c06efe..356e69fc 100644 --- a/src/leveldb/db/log_format.h +++ b/src/leveldb/db/log_format.h @@ -3,7 +3,7 @@ // found in the LICENSE file. See the AUTHORS file for names of contributors. // // Log format information shared by reader and writer. -// See ../doc/log_format.txt for more detail. +// See ../doc/log_format.md for more detail. #ifndef STORAGE_LEVELDB_DB_LOG_FORMAT_H_ #define STORAGE_LEVELDB_DB_LOG_FORMAT_H_ diff --git a/src/leveldb/db/log_reader.cc b/src/leveldb/db/log_reader.cc index 1c5bf1ed..8b6ad136 100644 --- a/src/leveldb/db/log_reader.cc +++ b/src/leveldb/db/log_reader.cc @@ -25,7 +25,8 @@ Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum, eof_(false), last_record_offset_(0), end_of_buffer_offset_(0), - initial_offset_(initial_offset) { + initial_offset_(initial_offset), + resyncing_(initial_offset > 0) { } Reader::~Reader() { @@ -72,9 +73,25 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) { Slice fragment; while (true) { - uint64_t physical_record_offset = end_of_buffer_offset_ - buffer_.size(); const unsigned int record_type = ReadPhysicalRecord(&fragment); + // ReadPhysicalRecord may have only had an empty trailer remaining in its + // internal buffer. Calculate the offset of the next physical record now + // that it has returned, properly accounting for its header size. + uint64_t physical_record_offset = + end_of_buffer_offset_ - buffer_.size() - kHeaderSize - fragment.size(); + + if (resyncing_) { + if (record_type == kMiddleType) { + continue; + } else if (record_type == kLastType) { + resyncing_ = false; + continue; + } else { + resyncing_ = false; + } + } + switch (record_type) { case kFullType: if (in_fragmented_record) { @@ -169,7 +186,7 @@ uint64_t Reader::LastRecordOffset() { } void Reader::ReportCorruption(uint64_t bytes, const char* reason) { - ReportDrop(bytes, Status::Corruption(reason)); + ReportDrop(bytes, Status::Corruption(reason, file_->GetName())); } void Reader::ReportDrop(uint64_t bytes, const Status& reason) { diff --git a/src/leveldb/db/log_reader.h b/src/leveldb/db/log_reader.h index 6aff7917..8389d61f 100644 --- a/src/leveldb/db/log_reader.h +++ b/src/leveldb/db/log_reader.h @@ -73,6 +73,11 @@ class Reader { // Offset at which to start looking for the first record to return uint64_t const initial_offset_; + // True if we are resynchronizing after a seek (initial_offset_ > 0). In + // particular, a run of kMiddleType and kLastType records can be silently + // skipped in this mode + bool resyncing_; + // Extend record types with the following special values enum { kEof = kMaxRecordType + 1, diff --git a/src/leveldb/db/log_test.cc b/src/leveldb/db/log_test.cc index dcf05626..48a59286 100644 --- a/src/leveldb/db/log_test.cc +++ b/src/leveldb/db/log_test.cc @@ -79,7 +79,7 @@ class LogTest { virtual Status Skip(uint64_t n) { if (n > contents_.size()) { contents_.clear(); - return Status::NotFound("in-memory file skipepd past end"); + return Status::NotFound("in-memory file skipped past end"); } contents_.remove_prefix(n); @@ -104,23 +104,34 @@ class LogTest { StringSource source_; ReportCollector report_; bool reading_; - Writer writer_; - Reader reader_; + Writer* writer_; + Reader* reader_; // Record metadata for testing initial offset functionality static size_t initial_offset_record_sizes_[]; static uint64_t initial_offset_last_record_offsets_[]; + static int num_initial_offset_records_; public: LogTest() : reading_(false), - writer_(&dest_), - reader_(&source_, &report_, true/*checksum*/, - 0/*initial_offset*/) { + writer_(new Writer(&dest_)), + reader_(new Reader(&source_, &report_, true/*checksum*/, + 0/*initial_offset*/)) { + } + + ~LogTest() { + delete writer_; + delete reader_; + } + + void ReopenForAppend() { + delete writer_; + writer_ = new Writer(&dest_, dest_.contents_.size()); } void Write(const std::string& msg) { ASSERT_TRUE(!reading_) << "Write() after starting to read"; - writer_.AddRecord(Slice(msg)); + writer_->AddRecord(Slice(msg)); } size_t WrittenBytes() const { @@ -134,7 +145,7 @@ class LogTest { } std::string scratch; Slice record; - if (reader_.ReadRecord(&record, &scratch)) { + if (reader_->ReadRecord(&record, &scratch)) { return record.ToString(); } else { return "EOF"; @@ -182,13 +193,18 @@ class LogTest { } void WriteInitialOffsetLog() { - for (int i = 0; i < 4; i++) { + for (int i = 0; i < num_initial_offset_records_; i++) { std::string record(initial_offset_record_sizes_[i], static_cast('a' + i)); Write(record); } } + void StartReadingAt(uint64_t initial_offset) { + delete reader_; + reader_ = new Reader(&source_, &report_, true/*checksum*/, initial_offset); + } + void CheckOffsetPastEndReturnsNoRecords(uint64_t offset_past_end) { WriteInitialOffsetLog(); reading_ = true; @@ -208,32 +224,48 @@ class LogTest { source_.contents_ = Slice(dest_.contents_); Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, initial_offset); - Slice record; - std::string scratch; - ASSERT_TRUE(offset_reader->ReadRecord(&record, &scratch)); - ASSERT_EQ(initial_offset_record_sizes_[expected_record_offset], - record.size()); - ASSERT_EQ(initial_offset_last_record_offsets_[expected_record_offset], - offset_reader->LastRecordOffset()); - ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]); + + // Read all records from expected_record_offset through the last one. + ASSERT_LT(expected_record_offset, num_initial_offset_records_); + for (; expected_record_offset < num_initial_offset_records_; + ++expected_record_offset) { + Slice record; + std::string scratch; + ASSERT_TRUE(offset_reader->ReadRecord(&record, &scratch)); + ASSERT_EQ(initial_offset_record_sizes_[expected_record_offset], + record.size()); + ASSERT_EQ(initial_offset_last_record_offsets_[expected_record_offset], + offset_reader->LastRecordOffset()); + ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]); + } delete offset_reader; } - }; size_t LogTest::initial_offset_record_sizes_[] = {10000, // Two sizable records in first block 10000, 2 * log::kBlockSize - 1000, // Span three blocks - 1}; + 1, + 13716, // Consume all but two bytes of block 3. + log::kBlockSize - kHeaderSize, // Consume the entirety of block 4. + }; uint64_t LogTest::initial_offset_last_record_offsets_[] = {0, kHeaderSize + 10000, 2 * (kHeaderSize + 10000), 2 * (kHeaderSize + 10000) + - (2 * log::kBlockSize - 1000) + 3 * kHeaderSize}; + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize, + 2 * (kHeaderSize + 10000) + + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize + + kHeaderSize + 1, + 3 * log::kBlockSize, + }; +// LogTest::initial_offset_last_record_offsets_ must be defined before this. +int LogTest::num_initial_offset_records_ = + sizeof(LogTest::initial_offset_last_record_offsets_)/sizeof(uint64_t); TEST(LogTest, Empty) { ASSERT_EQ("EOF", Read()); @@ -318,6 +350,15 @@ TEST(LogTest, AlignedEof) { ASSERT_EQ("EOF", Read()); } +TEST(LogTest, OpenForAppend) { + Write("hello"); + ReopenForAppend(); + Write("world"); + ASSERT_EQ("hello", Read()); + ASSERT_EQ("world", Read()); + ASSERT_EQ("EOF", Read()); +} + TEST(LogTest, RandomRead) { const int N = 500; Random write_rnd(301); @@ -445,6 +486,22 @@ TEST(LogTest, PartialLastIsIgnored) { ASSERT_EQ(0, DroppedBytes()); } +TEST(LogTest, SkipIntoMultiRecord) { + // Consider a fragmented record: + // first(R1), middle(R1), last(R1), first(R2) + // If initial_offset points to a record after first(R1) but before first(R2) + // incomplete fragment errors are not actual errors, and must be suppressed + // until a new first or full record is encountered. + Write(BigString("foo", 3*kBlockSize)); + Write("correct"); + StartReadingAt(kBlockSize); + + ASSERT_EQ("correct", Read()); + ASSERT_EQ("", ReportMessage()); + ASSERT_EQ(0, DroppedBytes()); + ASSERT_EQ("EOF", Read()); +} + TEST(LogTest, ErrorJoinsRecords) { // Consider two fragmented records: // first(R1) last(R1) first(R2) last(R2) @@ -514,6 +571,10 @@ TEST(LogTest, ReadFourthStart) { 3); } +TEST(LogTest, ReadInitialOffsetIntoBlockPadding) { + CheckInitialOffsetRecord(3 * log::kBlockSize - 3, 5); +} + TEST(LogTest, ReadEnd) { CheckOffsetPastEndReturnsNoRecords(0); } diff --git a/src/leveldb/db/log_writer.cc b/src/leveldb/db/log_writer.cc index 2da99ac0..74a03270 100644 --- a/src/leveldb/db/log_writer.cc +++ b/src/leveldb/db/log_writer.cc @@ -12,15 +12,24 @@ namespace leveldb { namespace log { -Writer::Writer(WritableFile* dest) - : dest_(dest), - block_offset_(0) { +static void InitTypeCrc(uint32_t* type_crc) { for (int i = 0; i <= kMaxRecordType; i++) { char t = static_cast(i); - type_crc_[i] = crc32c::Value(&t, 1); + type_crc[i] = crc32c::Value(&t, 1); } } +Writer::Writer(WritableFile* dest) + : dest_(dest), + block_offset_(0) { + InitTypeCrc(type_crc_); +} + +Writer::Writer(WritableFile* dest, uint64_t dest_length) + : dest_(dest), block_offset_(dest_length % kBlockSize) { + InitTypeCrc(type_crc_); +} + Writer::~Writer() { } diff --git a/src/leveldb/db/log_writer.h b/src/leveldb/db/log_writer.h index b1329e71..9e7cc470 100644 --- a/src/leveldb/db/log_writer.h +++ b/src/leveldb/db/log_writer.h @@ -23,6 +23,11 @@ class Writer { // "*dest" must remain live while this Writer is in use. explicit Writer(WritableFile* dest); + // Create a writer that will append data to "*dest". + // "*dest" must have initial length "dest_length". + // "*dest" must remain live while this Writer is in use. + Writer(WritableFile* dest, uint64_t dest_length); + ~Writer(); Status AddRecord(const Slice& slice); diff --git a/src/leveldb/db/memtable.cc b/src/leveldb/db/memtable.cc index bfec0a7e..287afdbd 100644 --- a/src/leveldb/db/memtable.cc +++ b/src/leveldb/db/memtable.cc @@ -101,7 +101,7 @@ void MemTable::Add(SequenceNumber s, ValueType type, p += 8; p = EncodeVarint32(p, val_size); memcpy(p, value.data(), val_size); - assert((p + val_size) - buf == encoded_len); + assert(p + val_size == buf + encoded_len); table_.Insert(buf); } diff --git a/src/leveldb/db/memtable.h b/src/leveldb/db/memtable.h index 92e90bb0..9f41567c 100644 --- a/src/leveldb/db/memtable.h +++ b/src/leveldb/db/memtable.h @@ -36,10 +36,7 @@ class MemTable { } // Returns an estimate of the number of bytes of data in use by this - // data structure. - // - // REQUIRES: external synchronization to prevent simultaneous - // operations on the same MemTable. + // data structure. It is safe to call when MemTable is being modified. size_t ApproximateMemoryUsage(); // Return an iterator that yields the contents of the memtable. diff --git a/src/leveldb/db/recovery_test.cc b/src/leveldb/db/recovery_test.cc new file mode 100644 index 00000000..9596f428 --- /dev/null +++ b/src/leveldb/db/recovery_test.cc @@ -0,0 +1,324 @@ +// Copyright (c) 2014 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/write_batch.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +class RecoveryTest { + public: + RecoveryTest() : env_(Env::Default()), db_(NULL) { + dbname_ = test::TmpDir() + "/recovery_test"; + DestroyDB(dbname_, Options()); + Open(); + } + + ~RecoveryTest() { + Close(); + DestroyDB(dbname_, Options()); + } + + DBImpl* dbfull() const { return reinterpret_cast(db_); } + Env* env() const { return env_; } + + bool CanAppend() { + WritableFile* tmp; + Status s = env_->NewAppendableFile(CurrentFileName(dbname_), &tmp); + delete tmp; + if (s.IsNotSupportedError()) { + return false; + } else { + return true; + } + } + + void Close() { + delete db_; + db_ = NULL; + } + + void Open(Options* options = NULL) { + Close(); + Options opts; + if (options != NULL) { + opts = *options; + } else { + opts.reuse_logs = true; // TODO(sanjay): test both ways + opts.create_if_missing = true; + } + if (opts.env == NULL) { + opts.env = env_; + } + ASSERT_OK(DB::Open(opts, dbname_, &db_)); + ASSERT_EQ(1, NumLogs()); + } + + Status Put(const std::string& k, const std::string& v) { + return db_->Put(WriteOptions(), k, v); + } + + std::string Get(const std::string& k, const Snapshot* snapshot = NULL) { + std::string result; + Status s = db_->Get(ReadOptions(), k, &result); + if (s.IsNotFound()) { + result = "NOT_FOUND"; + } else if (!s.ok()) { + result = s.ToString(); + } + return result; + } + + std::string ManifestFileName() { + std::string current; + ASSERT_OK(ReadFileToString(env_, CurrentFileName(dbname_), ¤t)); + size_t len = current.size(); + if (len > 0 && current[len-1] == '\n') { + current.resize(len - 1); + } + return dbname_ + "/" + current; + } + + std::string LogName(uint64_t number) { + return LogFileName(dbname_, number); + } + + size_t DeleteLogFiles() { + std::vector logs = GetFiles(kLogFile); + for (size_t i = 0; i < logs.size(); i++) { + ASSERT_OK(env_->DeleteFile(LogName(logs[i]))) << LogName(logs[i]); + } + return logs.size(); + } + + uint64_t FirstLogFile() { + return GetFiles(kLogFile)[0]; + } + + std::vector GetFiles(FileType t) { + std::vector filenames; + ASSERT_OK(env_->GetChildren(dbname_, &filenames)); + std::vector result; + for (size_t i = 0; i < filenames.size(); i++) { + uint64_t number; + FileType type; + if (ParseFileName(filenames[i], &number, &type) && type == t) { + result.push_back(number); + } + } + return result; + } + + int NumLogs() { + return GetFiles(kLogFile).size(); + } + + int NumTables() { + return GetFiles(kTableFile).size(); + } + + uint64_t FileSize(const std::string& fname) { + uint64_t result; + ASSERT_OK(env_->GetFileSize(fname, &result)) << fname; + return result; + } + + void CompactMemTable() { + dbfull()->TEST_CompactMemTable(); + } + + // Directly construct a log file that sets key to val. + void MakeLogFile(uint64_t lognum, SequenceNumber seq, Slice key, Slice val) { + std::string fname = LogFileName(dbname_, lognum); + WritableFile* file; + ASSERT_OK(env_->NewWritableFile(fname, &file)); + log::Writer writer(file); + WriteBatch batch; + batch.Put(key, val); + WriteBatchInternal::SetSequence(&batch, seq); + ASSERT_OK(writer.AddRecord(WriteBatchInternal::Contents(&batch))); + ASSERT_OK(file->Flush()); + delete file; + } + + private: + std::string dbname_; + Env* env_; + DB* db_; +}; + +TEST(RecoveryTest, ManifestReused) { + if (!CanAppend()) { + fprintf(stderr, "skipping test because env does not support appending\n"); + return; + } + ASSERT_OK(Put("foo", "bar")); + Close(); + std::string old_manifest = ManifestFileName(); + Open(); + ASSERT_EQ(old_manifest, ManifestFileName()); + ASSERT_EQ("bar", Get("foo")); + Open(); + ASSERT_EQ(old_manifest, ManifestFileName()); + ASSERT_EQ("bar", Get("foo")); +} + +TEST(RecoveryTest, LargeManifestCompacted) { + if (!CanAppend()) { + fprintf(stderr, "skipping test because env does not support appending\n"); + return; + } + ASSERT_OK(Put("foo", "bar")); + Close(); + std::string old_manifest = ManifestFileName(); + + // Pad with zeroes to make manifest file very big. + { + uint64_t len = FileSize(old_manifest); + WritableFile* file; + ASSERT_OK(env()->NewAppendableFile(old_manifest, &file)); + std::string zeroes(3*1048576 - static_cast(len), 0); + ASSERT_OK(file->Append(zeroes)); + ASSERT_OK(file->Flush()); + delete file; + } + + Open(); + std::string new_manifest = ManifestFileName(); + ASSERT_NE(old_manifest, new_manifest); + ASSERT_GT(10000, FileSize(new_manifest)); + ASSERT_EQ("bar", Get("foo")); + + Open(); + ASSERT_EQ(new_manifest, ManifestFileName()); + ASSERT_EQ("bar", Get("foo")); +} + +TEST(RecoveryTest, NoLogFiles) { + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ(1, DeleteLogFiles()); + Open(); + ASSERT_EQ("NOT_FOUND", Get("foo")); + Open(); + ASSERT_EQ("NOT_FOUND", Get("foo")); +} + +TEST(RecoveryTest, LogFileReuse) { + if (!CanAppend()) { + fprintf(stderr, "skipping test because env does not support appending\n"); + return; + } + for (int i = 0; i < 2; i++) { + ASSERT_OK(Put("foo", "bar")); + if (i == 0) { + // Compact to ensure current log is empty + CompactMemTable(); + } + Close(); + ASSERT_EQ(1, NumLogs()); + uint64_t number = FirstLogFile(); + if (i == 0) { + ASSERT_EQ(0, FileSize(LogName(number))); + } else { + ASSERT_LT(0, FileSize(LogName(number))); + } + Open(); + ASSERT_EQ(1, NumLogs()); + ASSERT_EQ(number, FirstLogFile()) << "did not reuse log file"; + ASSERT_EQ("bar", Get("foo")); + Open(); + ASSERT_EQ(1, NumLogs()); + ASSERT_EQ(number, FirstLogFile()) << "did not reuse log file"; + ASSERT_EQ("bar", Get("foo")); + } +} + +TEST(RecoveryTest, MultipleMemTables) { + // Make a large log. + const int kNum = 1000; + for (int i = 0; i < kNum; i++) { + char buf[100]; + snprintf(buf, sizeof(buf), "%050d", i); + ASSERT_OK(Put(buf, buf)); + } + ASSERT_EQ(0, NumTables()); + Close(); + ASSERT_EQ(0, NumTables()); + ASSERT_EQ(1, NumLogs()); + uint64_t old_log_file = FirstLogFile(); + + // Force creation of multiple memtables by reducing the write buffer size. + Options opt; + opt.reuse_logs = true; + opt.write_buffer_size = (kNum*100) / 2; + Open(&opt); + ASSERT_LE(2, NumTables()); + ASSERT_EQ(1, NumLogs()); + ASSERT_NE(old_log_file, FirstLogFile()) << "must not reuse log"; + for (int i = 0; i < kNum; i++) { + char buf[100]; + snprintf(buf, sizeof(buf), "%050d", i); + ASSERT_EQ(buf, Get(buf)); + } +} + +TEST(RecoveryTest, MultipleLogFiles) { + ASSERT_OK(Put("foo", "bar")); + Close(); + ASSERT_EQ(1, NumLogs()); + + // Make a bunch of uncompacted log files. + uint64_t old_log = FirstLogFile(); + MakeLogFile(old_log+1, 1000, "hello", "world"); + MakeLogFile(old_log+2, 1001, "hi", "there"); + MakeLogFile(old_log+3, 1002, "foo", "bar2"); + + // Recover and check that all log files were processed. + Open(); + ASSERT_LE(1, NumTables()); + ASSERT_EQ(1, NumLogs()); + uint64_t new_log = FirstLogFile(); + ASSERT_LE(old_log+3, new_log); + ASSERT_EQ("bar2", Get("foo")); + ASSERT_EQ("world", Get("hello")); + ASSERT_EQ("there", Get("hi")); + + // Test that previous recovery produced recoverable state. + Open(); + ASSERT_LE(1, NumTables()); + ASSERT_EQ(1, NumLogs()); + if (CanAppend()) { + ASSERT_EQ(new_log, FirstLogFile()); + } + ASSERT_EQ("bar2", Get("foo")); + ASSERT_EQ("world", Get("hello")); + ASSERT_EQ("there", Get("hi")); + + // Check that introducing an older log file does not cause it to be re-read. + Close(); + MakeLogFile(old_log+1, 2000, "hello", "stale write"); + Open(); + ASSERT_LE(1, NumTables()); + ASSERT_EQ(1, NumLogs()); + if (CanAppend()) { + ASSERT_EQ(new_log, FirstLogFile()); + } + ASSERT_EQ("bar2", Get("foo")); + ASSERT_EQ("world", Get("hello")); + ASSERT_EQ("there", Get("hi")); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/repair.cc b/src/leveldb/db/repair.cc index d490182a..7281e3d3 100644 --- a/src/leveldb/db/repair.cc +++ b/src/leveldb/db/repair.cc @@ -203,7 +203,7 @@ class Repairer { while (reader.ReadRecord(&record, &scratch)) { if (record.size() < 12) { reporter.Corruption( - record.size(), Status::Corruption("log record too small")); + record.size(), Status::Corruption("log record too small", logname)); continue; } WriteBatchInternal::SetContents(&batch, record); @@ -399,7 +399,7 @@ class Repairer { t.meta.smallest, t.meta.largest); } - //LogPrintf( "NewDescriptor:\n%s\n", edit_.DebugString().c_str()); + //fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str()); { log::Writer log(file); std::string record; diff --git a/src/leveldb/db/skiplist.h b/src/leveldb/db/skiplist.h index ed8b0922..8bd77764 100644 --- a/src/leveldb/db/skiplist.h +++ b/src/leveldb/db/skiplist.h @@ -1,10 +1,10 @@ -#ifndef STORAGE_LEVELDB_DB_SKIPLIST_H_ -#define STORAGE_LEVELDB_DB_SKIPLIST_H_ - // Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -// + +#ifndef STORAGE_LEVELDB_DB_SKIPLIST_H_ +#define STORAGE_LEVELDB_DB_SKIPLIST_H_ + // Thread safety // ------------- // diff --git a/src/leveldb/db/skiplist_test.cc b/src/leveldb/db/skiplist_test.cc index c78f4b4f..aee1461e 100644 --- a/src/leveldb/db/skiplist_test.cc +++ b/src/leveldb/db/skiplist_test.cc @@ -250,7 +250,7 @@ class ConcurrentTest { // Note that generation 0 is never inserted, so it is ok if // <*,0,*> is missing. ASSERT_TRUE((gen(pos) == 0) || - (gen(pos) > initial_state.Get(key(pos))) + (gen(pos) > static_cast(initial_state.Get(key(pos)))) ) << "key: " << key(pos) << "; gen: " << gen(pos) << "; initgen: " diff --git a/src/leveldb/db/snapshot.h b/src/leveldb/db/snapshot.h index e7f8fd2c..6ed413c4 100644 --- a/src/leveldb/db/snapshot.h +++ b/src/leveldb/db/snapshot.h @@ -5,6 +5,7 @@ #ifndef STORAGE_LEVELDB_DB_SNAPSHOT_H_ #define STORAGE_LEVELDB_DB_SNAPSHOT_H_ +#include "db/dbformat.h" #include "leveldb/db.h" namespace leveldb { diff --git a/src/leveldb/db/version_set.cc b/src/leveldb/db/version_set.cc index aa83df55..2cb6d80e 100644 --- a/src/leveldb/db/version_set.cc +++ b/src/leveldb/db/version_set.cc @@ -20,21 +20,29 @@ namespace leveldb { -static const int kTargetFileSize = 2 * 1048576; +static size_t TargetFileSize(const Options* options) { + return options->max_file_size; +} // Maximum bytes of overlaps in grandparent (i.e., level+2) before we // stop building a single file in a level->level+1 compaction. -static const int64_t kMaxGrandParentOverlapBytes = 10 * kTargetFileSize; +static int64_t MaxGrandParentOverlapBytes(const Options* options) { + return 10 * TargetFileSize(options); +} // Maximum number of bytes in all compacted files. We avoid expanding // the lower level file set of a compaction if it would make the // total compaction cover more than this many bytes. -static const int64_t kExpandedCompactionByteSizeLimit = 25 * kTargetFileSize; +static int64_t ExpandedCompactionByteSizeLimit(const Options* options) { + return 25 * TargetFileSize(options); +} -static double MaxBytesForLevel(int level) { +static double MaxBytesForLevel(const Options* options, int level) { // Note: the result for level zero is not really used since we set // the level-0 compaction threshold based on number of files. - double result = 10 * 1048576.0; // Result for both level-0 and level-1 + + // Result for both level-0 and level-1 + double result = 10. * 1048576.0; while (level > 1) { result *= 10; level--; @@ -42,8 +50,9 @@ static double MaxBytesForLevel(int level) { return result; } -static uint64_t MaxFileSizeForLevel(int level) { - return kTargetFileSize; // We could vary per level to reduce number of files? +static uint64_t MaxFileSizeForLevel(const Options* options, int level) { + // We could vary per level to reduce number of files? + return TargetFileSize(options); } static int64_t TotalFileSize(const std::vector& files) { @@ -508,7 +517,7 @@ int Version::PickLevelForMemTableOutput( // Check that file does not overlap too many grandparent bytes. GetOverlappingInputs(level + 2, &start, &limit, &overlaps); const int64_t sum = TotalFileSize(overlaps); - if (sum > kMaxGrandParentOverlapBytes) { + if (sum > MaxGrandParentOverlapBytes(vset_->options_)) { break; } } @@ -893,7 +902,7 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) { return s; } -Status VersionSet::Recover() { +Status VersionSet::Recover(bool *save_manifest) { struct LogReporter : public log::Reader::Reporter { Status* status; virtual void Corruption(size_t bytes, const Status& s) { @@ -1003,11 +1012,49 @@ Status VersionSet::Recover() { last_sequence_ = last_sequence; log_number_ = log_number; prev_log_number_ = prev_log_number; + + // See if we can reuse the existing MANIFEST file. + if (ReuseManifest(dscname, current)) { + // No need to save new manifest + } else { + *save_manifest = true; + } } return s; } +bool VersionSet::ReuseManifest(const std::string& dscname, + const std::string& dscbase) { + if (!options_->reuse_logs) { + return false; + } + FileType manifest_type; + uint64_t manifest_number; + uint64_t manifest_size; + if (!ParseFileName(dscbase, &manifest_number, &manifest_type) || + manifest_type != kDescriptorFile || + !env_->GetFileSize(dscname, &manifest_size).ok() || + // Make new compacted MANIFEST if old one is too big + manifest_size >= TargetFileSize(options_)) { + return false; + } + + assert(descriptor_file_ == NULL); + assert(descriptor_log_ == NULL); + Status r = env_->NewAppendableFile(dscname, &descriptor_file_); + if (!r.ok()) { + Log(options_->info_log, "Reuse MANIFEST: %s\n", r.ToString().c_str()); + assert(descriptor_file_ == NULL); + return false; + } + + Log(options_->info_log, "Reusing MANIFEST %s\n", dscname.c_str()); + descriptor_log_ = new log::Writer(descriptor_file_, manifest_size); + manifest_file_number_ = manifest_number; + return true; +} + void VersionSet::MarkFileNumberUsed(uint64_t number) { if (next_file_number_ <= number) { next_file_number_ = number + 1; @@ -1038,7 +1085,8 @@ void VersionSet::Finalize(Version* v) { } else { // Compute the ratio of current size to size limit. const uint64_t level_bytes = TotalFileSize(v->files_[level]); - score = static_cast(level_bytes) / MaxBytesForLevel(level); + score = + static_cast(level_bytes) / MaxBytesForLevel(options_, level); } if (score > best_score) { @@ -1252,7 +1300,7 @@ Compaction* VersionSet::PickCompaction() { level = current_->compaction_level_; assert(level >= 0); assert(level+1 < config::kNumLevels); - c = new Compaction(level); + c = new Compaction(options_, level); // Pick the first file that comes after compact_pointer_[level] for (size_t i = 0; i < current_->files_[level].size(); i++) { @@ -1269,7 +1317,7 @@ Compaction* VersionSet::PickCompaction() { } } else if (seek_compaction) { level = current_->file_to_compact_level_; - c = new Compaction(level); + c = new Compaction(options_, level); c->inputs_[0].push_back(current_->file_to_compact_); } else { return NULL; @@ -1314,7 +1362,8 @@ void VersionSet::SetupOtherInputs(Compaction* c) { const int64_t inputs1_size = TotalFileSize(c->inputs_[1]); const int64_t expanded0_size = TotalFileSize(expanded0); if (expanded0.size() > c->inputs_[0].size() && - inputs1_size + expanded0_size < kExpandedCompactionByteSizeLimit) { + inputs1_size + expanded0_size < + ExpandedCompactionByteSizeLimit(options_)) { InternalKey new_start, new_limit; GetRange(expanded0, &new_start, &new_limit); std::vector expanded1; @@ -1376,7 +1425,7 @@ Compaction* VersionSet::CompactRange( // and we must not pick one file and drop another older file if the // two files overlap. if (level > 0) { - const uint64_t limit = MaxFileSizeForLevel(level); + const uint64_t limit = MaxFileSizeForLevel(options_, level); uint64_t total = 0; for (size_t i = 0; i < inputs.size(); i++) { uint64_t s = inputs[i]->file_size; @@ -1388,7 +1437,7 @@ Compaction* VersionSet::CompactRange( } } - Compaction* c = new Compaction(level); + Compaction* c = new Compaction(options_, level); c->input_version_ = current_; c->input_version_->Ref(); c->inputs_[0] = inputs; @@ -1396,9 +1445,9 @@ Compaction* VersionSet::CompactRange( return c; } -Compaction::Compaction(int level) +Compaction::Compaction(const Options* options, int level) : level_(level), - max_output_file_size_(MaxFileSizeForLevel(level)), + max_output_file_size_(MaxFileSizeForLevel(options, level)), input_version_(NULL), grandparent_index_(0), seen_key_(false), @@ -1415,12 +1464,13 @@ Compaction::~Compaction() { } bool Compaction::IsTrivialMove() const { + const VersionSet* vset = input_version_->vset_; // Avoid a move if there is lots of overlapping grandparent data. // Otherwise, the move could create a parent file that will require // a very expensive merge later on. - return (num_input_files(0) == 1 && - num_input_files(1) == 0 && - TotalFileSize(grandparents_) <= kMaxGrandParentOverlapBytes); + return (num_input_files(0) == 1 && num_input_files(1) == 0 && + TotalFileSize(grandparents_) <= + MaxGrandParentOverlapBytes(vset->options_)); } void Compaction::AddInputDeletions(VersionEdit* edit) { @@ -1453,8 +1503,9 @@ bool Compaction::IsBaseLevelForKey(const Slice& user_key) { } bool Compaction::ShouldStopBefore(const Slice& internal_key) { + const VersionSet* vset = input_version_->vset_; // Scan to find earliest grandparent file that contains key. - const InternalKeyComparator* icmp = &input_version_->vset_->icmp_; + const InternalKeyComparator* icmp = &vset->icmp_; while (grandparent_index_ < grandparents_.size() && icmp->Compare(internal_key, grandparents_[grandparent_index_]->largest.Encode()) > 0) { @@ -1465,7 +1516,7 @@ bool Compaction::ShouldStopBefore(const Slice& internal_key) { } seen_key_ = true; - if (overlapped_bytes_ > kMaxGrandParentOverlapBytes) { + if (overlapped_bytes_ > MaxGrandParentOverlapBytes(vset->options_)) { // Too much overlap for current output; start new output overlapped_bytes_ = 0; return true; diff --git a/src/leveldb/db/version_set.h b/src/leveldb/db/version_set.h index 8dc14b8e..7935a965 100644 --- a/src/leveldb/db/version_set.h +++ b/src/leveldb/db/version_set.h @@ -179,7 +179,7 @@ class VersionSet { EXCLUSIVE_LOCKS_REQUIRED(mu); // Recover the last saved descriptor from persistent storage. - Status Recover(); + Status Recover(bool *save_manifest); // Return the current version. Version* current() const { return current_; } @@ -274,6 +274,8 @@ class VersionSet { friend class Compaction; friend class Version; + bool ReuseManifest(const std::string& dscname, const std::string& dscbase); + void Finalize(Version* v); void GetRange(const std::vector& inputs, @@ -364,7 +366,7 @@ class Compaction { friend class Version; friend class VersionSet; - explicit Compaction(int level); + Compaction(const Options* options, int level); int level_; uint64_t max_output_file_size_; @@ -374,7 +376,7 @@ class Compaction { // Each compaction reads inputs from "level_" and "level_+1" std::vector inputs_[2]; // The two sets of inputs - // State used to check for number of of overlapping grandparent files + // State used to check for number of overlapping grandparent files // (parent == level_ + 1, grandparent == level_ + 2) std::vector grandparents_; size_t grandparent_index_; // Index in grandparent_starts_ diff --git a/src/leveldb/db/write_batch_internal.h b/src/leveldb/db/write_batch_internal.h index 310a3c89..9448ef7b 100644 --- a/src/leveldb/db/write_batch_internal.h +++ b/src/leveldb/db/write_batch_internal.h @@ -5,6 +5,7 @@ #ifndef STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ #define STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ +#include "db/dbformat.h" #include "leveldb/write_batch.h" namespace leveldb { diff --git a/src/leveldb/doc/doc.css b/src/leveldb/doc/doc.css deleted file mode 100644 index 700c564e..00000000 --- a/src/leveldb/doc/doc.css +++ /dev/null @@ -1,89 +0,0 @@ -body { - margin-left: 0.5in; - margin-right: 0.5in; - background: white; - color: black; -} - -h1 { - margin-left: -0.2in; - font-size: 14pt; -} -h2 { - margin-left: -0in; - font-size: 12pt; -} -h3 { - margin-left: -0in; -} -h4 { - margin-left: -0in; -} -hr { - margin-left: -0in; -} - -/* Definition lists: definition term bold */ -dt { - font-weight: bold; -} - -address { - text-align: center; -} -code,samp,var { - color: blue; -} -kbd { - color: #600000; -} -div.note p { - float: right; - width: 3in; - margin-right: 0%; - padding: 1px; - border: 2px solid #6060a0; - background-color: #fffff0; -} - -ul { - margin-top: -0em; - margin-bottom: -0em; -} - -ol { - margin-top: -0em; - margin-bottom: -0em; -} - -UL.nobullets { - list-style-type: none; - list-style-image: none; - margin-left: -1em; -} - -p { - margin: 1em 0 1em 0; - padding: 0 0 0 0; -} - -pre { - line-height: 1.3em; - padding: 0.4em 0 0.8em 0; - margin: 0 0 0 0; - border: 0 0 0 0; - color: blue; -} - -.datatable { - margin-left: auto; - margin-right: auto; - margin-top: 2em; - margin-bottom: 2em; - border: 1px solid; -} - -.datatable td,th { - padding: 0 0.5em 0 0.5em; - text-align: right; -} diff --git a/src/leveldb/doc/impl.html b/src/leveldb/doc/impl.html deleted file mode 100644 index 6a468be0..00000000 --- a/src/leveldb/doc/impl.html +++ /dev/null @@ -1,213 +0,0 @@ - - - - -Leveldb file layout and compactions - - - - -

Files

- -The implementation of leveldb is similar in spirit to the -representation of a single - -Bigtable tablet (section 5.3). -However the organization of the files that make up the representation -is somewhat different and is explained below. - -

-Each database is represented by a set of files stored in a directory. -There are several different types of files as documented below: -

-

Log files

-

-A log file (*.log) stores a sequence of recent updates. Each update -is appended to the current log file. When the log file reaches a -pre-determined size (approximately 4MB by default), it is converted -to a sorted table (see below) and a new log file is created for future -updates. -

-A copy of the current log file is kept in an in-memory structure (the -memtable). This copy is consulted on every read so that read -operations reflect all logged updates. -

-

Sorted tables

-

-A sorted table (*.sst) stores a sequence of entries sorted by key. -Each entry is either a value for the key, or a deletion marker for the -key. (Deletion markers are kept around to hide obsolete values -present in older sorted tables). -

-The set of sorted tables are organized into a sequence of levels. The -sorted table generated from a log file is placed in a special young -level (also called level-0). When the number of young files exceeds a -certain threshold (currently four), all of the young files are merged -together with all of the overlapping level-1 files to produce a -sequence of new level-1 files (we create a new level-1 file for every -2MB of data.) -

-Files in the young level may contain overlapping keys. However files -in other levels have distinct non-overlapping key ranges. Consider -level number L where L >= 1. When the combined size of files in -level-L exceeds (10^L) MB (i.e., 10MB for level-1, 100MB for level-2, -...), one file in level-L, and all of the overlapping files in -level-(L+1) are merged to form a set of new files for level-(L+1). -These merges have the effect of gradually migrating new updates from -the young level to the largest level using only bulk reads and writes -(i.e., minimizing expensive seeks). - -

Manifest

-

-A MANIFEST file lists the set of sorted tables that make up each -level, the corresponding key ranges, and other important metadata. -A new MANIFEST file (with a new number embedded in the file name) -is created whenever the database is reopened. The MANIFEST file is -formatted as a log, and changes made to the serving state (as files -are added or removed) are appended to this log. -

-

Current

-

-CURRENT is a simple text file that contains the name of the latest -MANIFEST file. -

-

Info logs

-

-Informational messages are printed to files named LOG and LOG.old. -

-

Others

-

-Other files used for miscellaneous purposes may also be present -(LOCK, *.dbtmp). - -

Level 0

-When the log file grows above a certain size (1MB by default): -
    -
  • Create a brand new memtable and log file and direct future updates here -
  • In the background: -
      -
    • Write the contents of the previous memtable to an sstable -
    • Discard the memtable -
    • Delete the old log file and the old memtable -
    • Add the new sstable to the young (level-0) level. -
    -
- -

Compactions

- -

-When the size of level L exceeds its limit, we compact it in a -background thread. The compaction picks a file from level L and all -overlapping files from the next level L+1. Note that if a level-L -file overlaps only part of a level-(L+1) file, the entire file at -level-(L+1) is used as an input to the compaction and will be -discarded after the compaction. Aside: because level-0 is special -(files in it may overlap each other), we treat compactions from -level-0 to level-1 specially: a level-0 compaction may pick more than -one level-0 file in case some of these files overlap each other. - -

-A compaction merges the contents of the picked files to produce a -sequence of level-(L+1) files. We switch to producing a new -level-(L+1) file after the current output file has reached the target -file size (2MB). We also switch to a new output file when the key -range of the current output file has grown enough to overlap more than -ten level-(L+2) files. This last rule ensures that a later compaction -of a level-(L+1) file will not pick up too much data from level-(L+2). - -

-The old files are discarded and the new files are added to the serving -state. - -

-Compactions for a particular level rotate through the key space. In -more detail, for each level L, we remember the ending key of the last -compaction at level L. The next compaction for level L will pick the -first file that starts after this key (wrapping around to the -beginning of the key space if there is no such file). - -

-Compactions drop overwritten values. They also drop deletion markers -if there are no higher numbered levels that contain a file whose range -overlaps the current key. - -

Timing

- -Level-0 compactions will read up to four 1MB files from level-0, and -at worst all the level-1 files (10MB). I.e., we will read 14MB and -write 14MB. - -

-Other than the special level-0 compactions, we will pick one 2MB file -from level L. In the worst case, this will overlap ~ 12 files from -level L+1 (10 because level-(L+1) is ten times the size of level-L, -and another two at the boundaries since the file ranges at level-L -will usually not be aligned with the file ranges at level-L+1). The -compaction will therefore read 26MB and write 26MB. Assuming a disk -IO rate of 100MB/s (ballpark range for modern drives), the worst -compaction cost will be approximately 0.5 second. - -

-If we throttle the background writing to something small, say 10% of -the full 100MB/s speed, a compaction may take up to 5 seconds. If the -user is writing at 10MB/s, we might build up lots of level-0 files -(~50 to hold the 5*10MB). This may significantly increase the cost of -reads due to the overhead of merging more files together on every -read. - -

-Solution 1: To reduce this problem, we might want to increase the log -switching threshold when the number of level-0 files is large. Though -the downside is that the larger this threshold, the more memory we will -need to hold the corresponding memtable. - -

-Solution 2: We might want to decrease write rate artificially when the -number of level-0 files goes up. - -

-Solution 3: We work on reducing the cost of very wide merges. -Perhaps most of the level-0 files will have their blocks sitting -uncompressed in the cache and we will only need to worry about the -O(N) complexity in the merging iterator. - -

Number of files

- -Instead of always making 2MB files, we could make larger files for -larger levels to reduce the total file count, though at the expense of -more bursty compactions. Alternatively, we could shard the set of -files into multiple directories. - -

-An experiment on an ext3 filesystem on Feb 04, 2011 shows -the following timings to do 100K file opens in directories with -varying number of files: - - - - - -
Files in directoryMicroseconds to open a file
10009
1000010
10000016
-So maybe even the sharding is not necessary on modern filesystems? - -

Recovery

- -
    -
  • Read CURRENT to find name of the latest committed MANIFEST -
  • Read the named MANIFEST file -
  • Clean up stale files -
  • We could open all sstables here, but it is probably better to be lazy... -
  • Convert log chunk to a new level-0 sstable -
  • Start directing new writes to a new log file with recovered sequence# -
- -

Garbage collection of files

- -DeleteObsoleteFiles() is called at the end of every -compaction and at the end of recovery. It finds the names of all -files in the database. It deletes all log files that are not the -current log file. It deletes all table files that are not referenced -from some level and are not the output of an active compaction. - - - diff --git a/src/leveldb/doc/impl.md b/src/leveldb/doc/impl.md new file mode 100644 index 00000000..4b13f2a6 --- /dev/null +++ b/src/leveldb/doc/impl.md @@ -0,0 +1,170 @@ +## Files + +The implementation of leveldb is similar in spirit to the representation of a +single [Bigtable tablet (section 5.3)](http://research.google.com/archive/bigtable.html). +However the organization of the files that make up the representation is +somewhat different and is explained below. + +Each database is represented by a set of files stored in a directory. There are +several different types of files as documented below: + +### Log files + +A log file (*.log) stores a sequence of recent updates. Each update is appended +to the current log file. When the log file reaches a pre-determined size +(approximately 4MB by default), it is converted to a sorted table (see below) +and a new log file is created for future updates. + +A copy of the current log file is kept in an in-memory structure (the +`memtable`). This copy is consulted on every read so that read operations +reflect all logged updates. + +## Sorted tables + +A sorted table (*.ldb) stores a sequence of entries sorted by key. Each entry is +either a value for the key, or a deletion marker for the key. (Deletion markers +are kept around to hide obsolete values present in older sorted tables). + +The set of sorted tables are organized into a sequence of levels. The sorted +table generated from a log file is placed in a special **young** level (also +called level-0). When the number of young files exceeds a certain threshold +(currently four), all of the young files are merged together with all of the +overlapping level-1 files to produce a sequence of new level-1 files (we create +a new level-1 file for every 2MB of data.) + +Files in the young level may contain overlapping keys. However files in other +levels have distinct non-overlapping key ranges. Consider level number L where +L >= 1. When the combined size of files in level-L exceeds (10^L) MB (i.e., 10MB +for level-1, 100MB for level-2, ...), one file in level-L, and all of the +overlapping files in level-(L+1) are merged to form a set of new files for +level-(L+1). These merges have the effect of gradually migrating new updates +from the young level to the largest level using only bulk reads and writes +(i.e., minimizing expensive seeks). + +### Manifest + +A MANIFEST file lists the set of sorted tables that make up each level, the +corresponding key ranges, and other important metadata. A new MANIFEST file +(with a new number embedded in the file name) is created whenever the database +is reopened. The MANIFEST file is formatted as a log, and changes made to the +serving state (as files are added or removed) are appended to this log. + +### Current + +CURRENT is a simple text file that contains the name of the latest MANIFEST +file. + +### Info logs + +Informational messages are printed to files named LOG and LOG.old. + +### Others + +Other files used for miscellaneous purposes may also be present (LOCK, *.dbtmp). + +## Level 0 + +When the log file grows above a certain size (1MB by default): +Create a brand new memtable and log file and direct future updates here +In the background: +Write the contents of the previous memtable to an sstable +Discard the memtable +Delete the old log file and the old memtable +Add the new sstable to the young (level-0) level. + +## Compactions + +When the size of level L exceeds its limit, we compact it in a background +thread. The compaction picks a file from level L and all overlapping files from +the next level L+1. Note that if a level-L file overlaps only part of a +level-(L+1) file, the entire file at level-(L+1) is used as an input to the +compaction and will be discarded after the compaction. Aside: because level-0 +is special (files in it may overlap each other), we treat compactions from +level-0 to level-1 specially: a level-0 compaction may pick more than one +level-0 file in case some of these files overlap each other. + +A compaction merges the contents of the picked files to produce a sequence of +level-(L+1) files. We switch to producing a new level-(L+1) file after the +current output file has reached the target file size (2MB). We also switch to a +new output file when the key range of the current output file has grown enough +to overlap more than ten level-(L+2) files. This last rule ensures that a later +compaction of a level-(L+1) file will not pick up too much data from +level-(L+2). + +The old files are discarded and the new files are added to the serving state. + +Compactions for a particular level rotate through the key space. In more detail, +for each level L, we remember the ending key of the last compaction at level L. +The next compaction for level L will pick the first file that starts after this +key (wrapping around to the beginning of the key space if there is no such +file). + +Compactions drop overwritten values. They also drop deletion markers if there +are no higher numbered levels that contain a file whose range overlaps the +current key. + +### Timing + +Level-0 compactions will read up to four 1MB files from level-0, and at worst +all the level-1 files (10MB). I.e., we will read 14MB and write 14MB. + +Other than the special level-0 compactions, we will pick one 2MB file from level +L. In the worst case, this will overlap ~ 12 files from level L+1 (10 because +level-(L+1) is ten times the size of level-L, and another two at the boundaries +since the file ranges at level-L will usually not be aligned with the file +ranges at level-L+1). The compaction will therefore read 26MB and write 26MB. +Assuming a disk IO rate of 100MB/s (ballpark range for modern drives), the worst +compaction cost will be approximately 0.5 second. + +If we throttle the background writing to something small, say 10% of the full +100MB/s speed, a compaction may take up to 5 seconds. If the user is writing at +10MB/s, we might build up lots of level-0 files (~50 to hold the 5*10MB). This +may significantly increase the cost of reads due to the overhead of merging more +files together on every read. + +Solution 1: To reduce this problem, we might want to increase the log switching +threshold when the number of level-0 files is large. Though the downside is that +the larger this threshold, the more memory we will need to hold the +corresponding memtable. + +Solution 2: We might want to decrease write rate artificially when the number of +level-0 files goes up. + +Solution 3: We work on reducing the cost of very wide merges. Perhaps most of +the level-0 files will have their blocks sitting uncompressed in the cache and +we will only need to worry about the O(N) complexity in the merging iterator. + +### Number of files + +Instead of always making 2MB files, we could make larger files for larger levels +to reduce the total file count, though at the expense of more bursty +compactions. Alternatively, we could shard the set of files into multiple +directories. + +An experiment on an ext3 filesystem on Feb 04, 2011 shows the following timings +to do 100K file opens in directories with varying number of files: + + +| Files in directory | Microseconds to open a file | +|-------------------:|----------------------------:| +| 1000 | 9 | +| 10000 | 10 | +| 100000 | 16 | + +So maybe even the sharding is not necessary on modern filesystems? + +## Recovery + +* Read CURRENT to find name of the latest committed MANIFEST +* Read the named MANIFEST file +* Clean up stale files +* We could open all sstables here, but it is probably better to be lazy... +* Convert log chunk to a new level-0 sstable +* Start directing new writes to a new log file with recovered sequence# + +## Garbage collection of files + +`DeleteObsoleteFiles()` is called at the end of every compaction and at the end +of recovery. It finds the names of all files in the database. It deletes all log +files that are not the current log file. It deletes all table files that are not +referenced from some level and are not the output of an active compaction. diff --git a/src/leveldb/doc/index.html b/src/leveldb/doc/index.html deleted file mode 100644 index 3ed0ed9d..00000000 --- a/src/leveldb/doc/index.html +++ /dev/null @@ -1,549 +0,0 @@ - - - - -Leveldb - - - -

Leveldb

-
Jeff Dean, Sanjay Ghemawat
-

-The leveldb library provides a persistent key value store. Keys and -values are arbitrary byte arrays. The keys are ordered within the key -value store according to a user-specified comparator function. - -

-

Opening A Database

-

-A leveldb database has a name which corresponds to a file system -directory. All of the contents of database are stored in this -directory. The following example shows how to open a database, -creating it if necessary: -

-

-  #include <assert>
-  #include "leveldb/db.h"
-
-  leveldb::DB* db;
-  leveldb::Options options;
-  options.create_if_missing = true;
-  leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
-  assert(status.ok());
-  ...
-
-If you want to raise an error if the database already exists, add -the following line before the leveldb::DB::Open call: -
-  options.error_if_exists = true;
-
-

Status

-

-You may have noticed the leveldb::Status type above. Values of this -type are returned by most functions in leveldb that may encounter an -error. You can check if such a result is ok, and also print an -associated error message: -

-

-   leveldb::Status s = ...;
-   if (!s.ok()) cerr << s.ToString() << endl;
-
-

Closing A Database

-

-When you are done with a database, just delete the database object. -Example: -

-

-  ... open the db as described above ...
-  ... do something with db ...
-  delete db;
-
-

Reads And Writes

-

-The database provides Put, Delete, and Get methods to -modify/query the database. For example, the following code -moves the value stored under key1 to key2. -

-  std::string value;
-  leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
-  if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value);
-  if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1);
-
- -

Atomic Updates

-

-Note that if the process dies after the Put of key2 but before the -delete of key1, the same value may be left stored under multiple keys. -Such problems can be avoided by using the WriteBatch class to -atomically apply a set of updates: -

-

-  #include "leveldb/write_batch.h"
-  ...
-  std::string value;
-  leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
-  if (s.ok()) {
-    leveldb::WriteBatch batch;
-    batch.Delete(key1);
-    batch.Put(key2, value);
-    s = db->Write(leveldb::WriteOptions(), &batch);
-  }
-
-The WriteBatch holds a sequence of edits to be made to the database, -and these edits within the batch are applied in order. Note that we -called Delete before Put so that if key1 is identical to key2, -we do not end up erroneously dropping the value entirely. -

-Apart from its atomicity benefits, WriteBatch may also be used to -speed up bulk updates by placing lots of individual mutations into the -same batch. - -

Synchronous Writes

-By default, each write to leveldb is asynchronous: it -returns after pushing the write from the process into the operating -system. The transfer from operating system memory to the underlying -persistent storage happens asynchronously. The sync flag -can be turned on for a particular write to make the write operation -not return until the data being written has been pushed all the way to -persistent storage. (On Posix systems, this is implemented by calling -either fsync(...) or fdatasync(...) or -msync(..., MS_SYNC) before the write operation returns.) -
-  leveldb::WriteOptions write_options;
-  write_options.sync = true;
-  db->Put(write_options, ...);
-
-Asynchronous writes are often more than a thousand times as fast as -synchronous writes. The downside of asynchronous writes is that a -crash of the machine may cause the last few updates to be lost. Note -that a crash of just the writing process (i.e., not a reboot) will not -cause any loss since even when sync is false, an update -is pushed from the process memory into the operating system before it -is considered done. - -

-Asynchronous writes can often be used safely. For example, when -loading a large amount of data into the database you can handle lost -updates by restarting the bulk load after a crash. A hybrid scheme is -also possible where every Nth write is synchronous, and in the event -of a crash, the bulk load is restarted just after the last synchronous -write finished by the previous run. (The synchronous write can update -a marker that describes where to restart on a crash.) - -

-WriteBatch provides an alternative to asynchronous writes. -Multiple updates may be placed in the same WriteBatch and -applied together using a synchronous write (i.e., -write_options.sync is set to true). The extra cost of -the synchronous write will be amortized across all of the writes in -the batch. - -

-

Concurrency

-

-A database may only be opened by one process at a time. -The leveldb implementation acquires a lock from the -operating system to prevent misuse. Within a single process, the -same leveldb::DB object may be safely shared by multiple -concurrent threads. I.e., different threads may write into or fetch -iterators or call Get on the same database without any -external synchronization (the leveldb implementation will -automatically do the required synchronization). However other objects -(like Iterator and WriteBatch) may require external synchronization. -If two threads share such an object, they must protect access to it -using their own locking protocol. More details are available in -the public header files. -

-

Iteration

-

-The following example demonstrates how to print all key,value pairs -in a database. -

-

-  leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
-  for (it->SeekToFirst(); it->Valid(); it->Next()) {
-    cout << it->key().ToString() << ": "  << it->value().ToString() << endl;
-  }
-  assert(it->status().ok());  // Check for any errors found during the scan
-  delete it;
-
-The following variation shows how to process just the keys in the -range [start,limit): -

-

-  for (it->Seek(start);
-       it->Valid() && it->key().ToString() < limit;
-       it->Next()) {
-    ...
-  }
-
-You can also process entries in reverse order. (Caveat: reverse -iteration may be somewhat slower than forward iteration.) -

-

-  for (it->SeekToLast(); it->Valid(); it->Prev()) {
-    ...
-  }
-
-

Snapshots

-

-Snapshots provide consistent read-only views over the entire state of -the key-value store. ReadOptions::snapshot may be non-NULL to indicate -that a read should operate on a particular version of the DB state. -If ReadOptions::snapshot is NULL, the read will operate on an -implicit snapshot of the current state. -

-Snapshots are created by the DB::GetSnapshot() method: -

-

-  leveldb::ReadOptions options;
-  options.snapshot = db->GetSnapshot();
-  ... apply some updates to db ...
-  leveldb::Iterator* iter = db->NewIterator(options);
-  ... read using iter to view the state when the snapshot was created ...
-  delete iter;
-  db->ReleaseSnapshot(options.snapshot);
-
-Note that when a snapshot is no longer needed, it should be released -using the DB::ReleaseSnapshot interface. This allows the -implementation to get rid of state that was being maintained just to -support reading as of that snapshot. -

Slice

-

-The return value of the it->key() and it->value() calls above -are instances of the leveldb::Slice type. Slice is a simple -structure that contains a length and a pointer to an external byte -array. Returning a Slice is a cheaper alternative to returning a -std::string since we do not need to copy potentially large keys and -values. In addition, leveldb methods do not return null-terminated -C-style strings since leveldb keys and values are allowed to -contain '\0' bytes. -

-C++ strings and null-terminated C-style strings can be easily converted -to a Slice: -

-

-   leveldb::Slice s1 = "hello";
-
-   std::string str("world");
-   leveldb::Slice s2 = str;
-
-A Slice can be easily converted back to a C++ string: -
-   std::string str = s1.ToString();
-   assert(str == std::string("hello"));
-
-Be careful when using Slices since it is up to the caller to ensure that -the external byte array into which the Slice points remains live while -the Slice is in use. For example, the following is buggy: -

-

-   leveldb::Slice slice;
-   if (...) {
-     std::string str = ...;
-     slice = str;
-   }
-   Use(slice);
-
-When the if statement goes out of scope, str will be destroyed and the -backing storage for slice will disappear. -

-

Comparators

-

-The preceding examples used the default ordering function for key, -which orders bytes lexicographically. You can however supply a custom -comparator when opening a database. For example, suppose each -database key consists of two numbers and we should sort by the first -number, breaking ties by the second number. First, define a proper -subclass of leveldb::Comparator that expresses these rules: -

-

-  class TwoPartComparator : public leveldb::Comparator {
-   public:
-    // Three-way comparison function:
-    //   if a < b: negative result
-    //   if a > b: positive result
-    //   else: zero result
-    int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const {
-      int a1, a2, b1, b2;
-      ParseKey(a, &a1, &a2);
-      ParseKey(b, &b1, &b2);
-      if (a1 < b1) return -1;
-      if (a1 > b1) return +1;
-      if (a2 < b2) return -1;
-      if (a2 > b2) return +1;
-      return 0;
-    }
-
-    // Ignore the following methods for now:
-    const char* Name() const { return "TwoPartComparator"; }
-    void FindShortestSeparator(std::string*, const leveldb::Slice&) const { }
-    void FindShortSuccessor(std::string*) const { }
-  };
-
-Now create a database using this custom comparator: -

-

-  TwoPartComparator cmp;
-  leveldb::DB* db;
-  leveldb::Options options;
-  options.create_if_missing = true;
-  options.comparator = &cmp;
-  leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
-  ...
-
-

Backwards compatibility

-

-The result of the comparator's Name method is attached to the -database when it is created, and is checked on every subsequent -database open. If the name changes, the leveldb::DB::Open call will -fail. Therefore, change the name if and only if the new key format -and comparison function are incompatible with existing databases, and -it is ok to discard the contents of all existing databases. -

-You can however still gradually evolve your key format over time with -a little bit of pre-planning. For example, you could store a version -number at the end of each key (one byte should suffice for most uses). -When you wish to switch to a new key format (e.g., adding an optional -third part to the keys processed by TwoPartComparator), -(a) keep the same comparator name (b) increment the version number -for new keys (c) change the comparator function so it uses the -version numbers found in the keys to decide how to interpret them. -

-

Performance

-

-Performance can be tuned by changing the default values of the -types defined in include/leveldb/options.h. - -

-

Block size

-

-leveldb groups adjacent keys together into the same block and such a -block is the unit of transfer to and from persistent storage. The -default block size is approximately 4096 uncompressed bytes. -Applications that mostly do bulk scans over the contents of the -database may wish to increase this size. Applications that do a lot -of point reads of small values may wish to switch to a smaller block -size if performance measurements indicate an improvement. There isn't -much benefit in using blocks smaller than one kilobyte, or larger than -a few megabytes. Also note that compression will be more effective -with larger block sizes. -

-

Compression

-

-Each block is individually compressed before being written to -persistent storage. Compression is on by default since the default -compression method is very fast, and is automatically disabled for -uncompressible data. In rare cases, applications may want to disable -compression entirely, but should only do so if benchmarks show a -performance improvement: -

-

-  leveldb::Options options;
-  options.compression = leveldb::kNoCompression;
-  ... leveldb::DB::Open(options, name, ...) ....
-
-

Cache

-

-The contents of the database are stored in a set of files in the -filesystem and each file stores a sequence of compressed blocks. If -options.cache is non-NULL, it is used to cache frequently used -uncompressed block contents. -

-

-  #include "leveldb/cache.h"
-
-  leveldb::Options options;
-  options.cache = leveldb::NewLRUCache(100 * 1048576);  // 100MB cache
-  leveldb::DB* db;
-  leveldb::DB::Open(options, name, &db);
-  ... use the db ...
-  delete db
-  delete options.cache;
-
-Note that the cache holds uncompressed data, and therefore it should -be sized according to application level data sizes, without any -reduction from compression. (Caching of compressed blocks is left to -the operating system buffer cache, or any custom Env -implementation provided by the client.) -

-When performing a bulk read, the application may wish to disable -caching so that the data processed by the bulk read does not end up -displacing most of the cached contents. A per-iterator option can be -used to achieve this: -

-

-  leveldb::ReadOptions options;
-  options.fill_cache = false;
-  leveldb::Iterator* it = db->NewIterator(options);
-  for (it->SeekToFirst(); it->Valid(); it->Next()) {
-    ...
-  }
-
-

Key Layout

-

-Note that the unit of disk transfer and caching is a block. Adjacent -keys (according to the database sort order) will usually be placed in -the same block. Therefore the application can improve its performance -by placing keys that are accessed together near each other and placing -infrequently used keys in a separate region of the key space. -

-For example, suppose we are implementing a simple file system on top -of leveldb. The types of entries we might wish to store are: -

-

-   filename -> permission-bits, length, list of file_block_ids
-   file_block_id -> data
-
-We might want to prefix filename keys with one letter (say '/') and the -file_block_id keys with a different letter (say '0') so that scans -over just the metadata do not force us to fetch and cache bulky file -contents. -

-

Filters

-

-Because of the way leveldb data is organized on disk, -a single Get() call may involve multiple reads from disk. -The optional FilterPolicy mechanism can be used to reduce -the number of disk reads substantially. -

-   leveldb::Options options;
-   options.filter_policy = NewBloomFilterPolicy(10);
-   leveldb::DB* db;
-   leveldb::DB::Open(options, "/tmp/testdb", &db);
-   ... use the database ...
-   delete db;
-   delete options.filter_policy;
-
-The preceding code associates a -Bloom filter -based filtering policy with the database. Bloom filter based -filtering relies on keeping some number of bits of data in memory per -key (in this case 10 bits per key since that is the argument we passed -to NewBloomFilterPolicy). This filter will reduce the number of unnecessary -disk reads needed for Get() calls by a factor of -approximately a 100. Increasing the bits per key will lead to a -larger reduction at the cost of more memory usage. We recommend that -applications whose working set does not fit in memory and that do a -lot of random reads set a filter policy. -

-If you are using a custom comparator, you should ensure that the filter -policy you are using is compatible with your comparator. For example, -consider a comparator that ignores trailing spaces when comparing keys. -NewBloomFilterPolicy must not be used with such a comparator. -Instead, the application should provide a custom filter policy that -also ignores trailing spaces. For example: -

-  class CustomFilterPolicy : public leveldb::FilterPolicy {
-   private:
-    FilterPolicy* builtin_policy_;
-   public:
-    CustomFilterPolicy() : builtin_policy_(NewBloomFilterPolicy(10)) { }
-    ~CustomFilterPolicy() { delete builtin_policy_; }
-
-    const char* Name() const { return "IgnoreTrailingSpacesFilter"; }
-
-    void CreateFilter(const Slice* keys, int n, std::string* dst) const {
-      // Use builtin bloom filter code after removing trailing spaces
-      std::vector<Slice> trimmed(n);
-      for (int i = 0; i < n; i++) {
-        trimmed[i] = RemoveTrailingSpaces(keys[i]);
-      }
-      return builtin_policy_->CreateFilter(&trimmed[i], n, dst);
-    }
-
-    bool KeyMayMatch(const Slice& key, const Slice& filter) const {
-      // Use builtin bloom filter code after removing trailing spaces
-      return builtin_policy_->KeyMayMatch(RemoveTrailingSpaces(key), filter);
-    }
-  };
-
-

-Advanced applications may provide a filter policy that does not use -a bloom filter but uses some other mechanism for summarizing a set -of keys. See leveldb/filter_policy.h for detail. -

-

Checksums

-

-leveldb associates checksums with all data it stores in the file system. -There are two separate controls provided over how aggressively these -checksums are verified: -

-

    -
  • ReadOptions::verify_checksums may be set to true to force - checksum verification of all data that is read from the file system on - behalf of a particular read. By default, no such verification is - done. -

    -

  • Options::paranoid_checks may be set to true before opening a - database to make the database implementation raise an error as soon as - it detects an internal corruption. Depending on which portion of the - database has been corrupted, the error may be raised when the database - is opened, or later by another database operation. By default, - paranoid checking is off so that the database can be used even if - parts of its persistent storage have been corrupted. -

    - If a database is corrupted (perhaps it cannot be opened when - paranoid checking is turned on), the leveldb::RepairDB function - may be used to recover as much of the data as possible -

    -

-

Approximate Sizes

-

-The GetApproximateSizes method can used to get the approximate -number of bytes of file system space used by one or more key ranges. -

-

-   leveldb::Range ranges[2];
-   ranges[0] = leveldb::Range("a", "c");
-   ranges[1] = leveldb::Range("x", "z");
-   uint64_t sizes[2];
-   leveldb::Status s = db->GetApproximateSizes(ranges, 2, sizes);
-
-The preceding call will set sizes[0] to the approximate number of -bytes of file system space used by the key range [a..c) and -sizes[1] to the approximate number of bytes used by the key range -[x..z). -

-

Environment

-

-All file operations (and other operating system calls) issued by the -leveldb implementation are routed through a leveldb::Env object. -Sophisticated clients may wish to provide their own Env -implementation to get better control. For example, an application may -introduce artificial delays in the file IO paths to limit the impact -of leveldb on other activities in the system. -

-

-  class SlowEnv : public leveldb::Env {
-    .. implementation of the Env interface ...
-  };
-
-  SlowEnv env;
-  leveldb::Options options;
-  options.env = &env;
-  Status s = leveldb::DB::Open(options, ...);
-
-

Porting

-

-leveldb may be ported to a new platform by providing platform -specific implementations of the types/methods/functions exported by -leveldb/port/port.h. See leveldb/port/port_example.h for more -details. -

-In addition, the new platform may need a new default leveldb::Env -implementation. See leveldb/util/env_posix.h for an example. - -

Other Information

- -

-Details about the leveldb implementation may be found in -the following documents: -

- - - diff --git a/src/leveldb/doc/index.md b/src/leveldb/doc/index.md new file mode 100644 index 00000000..be856969 --- /dev/null +++ b/src/leveldb/doc/index.md @@ -0,0 +1,523 @@ +leveldb +======= + +_Jeff Dean, Sanjay Ghemawat_ + +The leveldb library provides a persistent key value store. Keys and values are +arbitrary byte arrays. The keys are ordered within the key value store +according to a user-specified comparator function. + +## Opening A Database + +A leveldb database has a name which corresponds to a file system directory. All +of the contents of database are stored in this directory. The following example +shows how to open a database, creating it if necessary: + +```c++ +#include +#include "leveldb/db.h" + +leveldb::DB* db; +leveldb::Options options; +options.create_if_missing = true; +leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db); +assert(status.ok()); +... +``` + +If you want to raise an error if the database already exists, add the following +line before the `leveldb::DB::Open` call: + +```c++ +options.error_if_exists = true; +``` + +## Status + +You may have noticed the `leveldb::Status` type above. Values of this type are +returned by most functions in leveldb that may encounter an error. You can check +if such a result is ok, and also print an associated error message: + +```c++ +leveldb::Status s = ...; +if (!s.ok()) cerr << s.ToString() << endl; +``` + +## Closing A Database + +When you are done with a database, just delete the database object. Example: + +```c++ +... open the db as described above ... +... do something with db ... +delete db; +``` + +## Reads And Writes + +The database provides Put, Delete, and Get methods to modify/query the database. +For example, the following code moves the value stored under key1 to key2. + +```c++ +std::string value; +leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value); +if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value); +if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1); +``` + +## Atomic Updates + +Note that if the process dies after the Put of key2 but before the delete of +key1, the same value may be left stored under multiple keys. Such problems can +be avoided by using the `WriteBatch` class to atomically apply a set of updates: + +```c++ +#include "leveldb/write_batch.h" +... +std::string value; +leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value); +if (s.ok()) { + leveldb::WriteBatch batch; + batch.Delete(key1); + batch.Put(key2, value); + s = db->Write(leveldb::WriteOptions(), &batch); +} +``` + +The `WriteBatch` holds a sequence of edits to be made to the database, and these +edits within the batch are applied in order. Note that we called Delete before +Put so that if key1 is identical to key2, we do not end up erroneously dropping +the value entirely. + +Apart from its atomicity benefits, `WriteBatch` may also be used to speed up +bulk updates by placing lots of individual mutations into the same batch. + +## Synchronous Writes + +By default, each write to leveldb is asynchronous: it returns after pushing the +write from the process into the operating system. The transfer from operating +system memory to the underlying persistent storage happens asynchronously. The +sync flag can be turned on for a particular write to make the write operation +not return until the data being written has been pushed all the way to +persistent storage. (On Posix systems, this is implemented by calling either +`fsync(...)` or `fdatasync(...)` or `msync(..., MS_SYNC)` before the write +operation returns.) + +```c++ +leveldb::WriteOptions write_options; +write_options.sync = true; +db->Put(write_options, ...); +``` + +Asynchronous writes are often more than a thousand times as fast as synchronous +writes. The downside of asynchronous writes is that a crash of the machine may +cause the last few updates to be lost. Note that a crash of just the writing +process (i.e., not a reboot) will not cause any loss since even when sync is +false, an update is pushed from the process memory into the operating system +before it is considered done. + +Asynchronous writes can often be used safely. For example, when loading a large +amount of data into the database you can handle lost updates by restarting the +bulk load after a crash. A hybrid scheme is also possible where every Nth write +is synchronous, and in the event of a crash, the bulk load is restarted just +after the last synchronous write finished by the previous run. (The synchronous +write can update a marker that describes where to restart on a crash.) + +`WriteBatch` provides an alternative to asynchronous writes. Multiple updates +may be placed in the same WriteBatch and applied together using a synchronous +write (i.e., `write_options.sync` is set to true). The extra cost of the +synchronous write will be amortized across all of the writes in the batch. + +## Concurrency + +A database may only be opened by one process at a time. The leveldb +implementation acquires a lock from the operating system to prevent misuse. +Within a single process, the same `leveldb::DB` object may be safely shared by +multiple concurrent threads. I.e., different threads may write into or fetch +iterators or call Get on the same database without any external synchronization +(the leveldb implementation will automatically do the required synchronization). +However other objects (like Iterator and `WriteBatch`) may require external +synchronization. If two threads share such an object, they must protect access +to it using their own locking protocol. More details are available in the public +header files. + +## Iteration + +The following example demonstrates how to print all key,value pairs in a +database. + +```c++ +leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions()); +for (it->SeekToFirst(); it->Valid(); it->Next()) { + cout << it->key().ToString() << ": " << it->value().ToString() << endl; +} +assert(it->status().ok()); // Check for any errors found during the scan +delete it; +``` + +The following variation shows how to process just the keys in the range +[start,limit): + +```c++ +for (it->Seek(start); + it->Valid() && it->key().ToString() < limit; + it->Next()) { + ... +} +``` + +You can also process entries in reverse order. (Caveat: reverse iteration may be +somewhat slower than forward iteration.) + +```c++ +for (it->SeekToLast(); it->Valid(); it->Prev()) { + ... +} +``` + +## Snapshots + +Snapshots provide consistent read-only views over the entire state of the +key-value store. `ReadOptions::snapshot` may be non-NULL to indicate that a +read should operate on a particular version of the DB state. If +`ReadOptions::snapshot` is NULL, the read will operate on an implicit snapshot +of the current state. + +Snapshots are created by the `DB::GetSnapshot()` method: + +```c++ +leveldb::ReadOptions options; +options.snapshot = db->GetSnapshot(); +... apply some updates to db ... +leveldb::Iterator* iter = db->NewIterator(options); +... read using iter to view the state when the snapshot was created ... +delete iter; +db->ReleaseSnapshot(options.snapshot); +``` + +Note that when a snapshot is no longer needed, it should be released using the +`DB::ReleaseSnapshot` interface. This allows the implementation to get rid of +state that was being maintained just to support reading as of that snapshot. + +## Slice + +The return value of the `it->key()` and `it->value()` calls above are instances +of the `leveldb::Slice` type. Slice is a simple structure that contains a length +and a pointer to an external byte array. Returning a Slice is a cheaper +alternative to returning a `std::string` since we do not need to copy +potentially large keys and values. In addition, leveldb methods do not return +null-terminated C-style strings since leveldb keys and values are allowed to +contain `'\0'` bytes. + +C++ strings and null-terminated C-style strings can be easily converted to a +Slice: + +```c++ +leveldb::Slice s1 = "hello"; + +std::string str("world"); +leveldb::Slice s2 = str; +``` + +A Slice can be easily converted back to a C++ string: + +```c++ +std::string str = s1.ToString(); +assert(str == std::string("hello")); +``` + +Be careful when using Slices since it is up to the caller to ensure that the +external byte array into which the Slice points remains live while the Slice is +in use. For example, the following is buggy: + +```c++ +leveldb::Slice slice; +if (...) { + std::string str = ...; + slice = str; +} +Use(slice); +``` + +When the if statement goes out of scope, str will be destroyed and the backing +storage for slice will disappear. + +## Comparators + +The preceding examples used the default ordering function for key, which orders +bytes lexicographically. You can however supply a custom comparator when opening +a database. For example, suppose each database key consists of two numbers and +we should sort by the first number, breaking ties by the second number. First, +define a proper subclass of `leveldb::Comparator` that expresses these rules: + +```c++ +class TwoPartComparator : public leveldb::Comparator { + public: + // Three-way comparison function: + // if a < b: negative result + // if a > b: positive result + // else: zero result + int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const { + int a1, a2, b1, b2; + ParseKey(a, &a1, &a2); + ParseKey(b, &b1, &b2); + if (a1 < b1) return -1; + if (a1 > b1) return +1; + if (a2 < b2) return -1; + if (a2 > b2) return +1; + return 0; + } + + // Ignore the following methods for now: + const char* Name() const { return "TwoPartComparator"; } + void FindShortestSeparator(std::string*, const leveldb::Slice&) const {} + void FindShortSuccessor(std::string*) const {} +}; +``` + +Now create a database using this custom comparator: + +```c++ +TwoPartComparator cmp; +leveldb::DB* db; +leveldb::Options options; +options.create_if_missing = true; +options.comparator = &cmp; +leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db); +... +``` + +### Backwards compatibility + +The result of the comparator's Name method is attached to the database when it +is created, and is checked on every subsequent database open. If the name +changes, the `leveldb::DB::Open` call will fail. Therefore, change the name if +and only if the new key format and comparison function are incompatible with +existing databases, and it is ok to discard the contents of all existing +databases. + +You can however still gradually evolve your key format over time with a little +bit of pre-planning. For example, you could store a version number at the end of +each key (one byte should suffice for most uses). When you wish to switch to a +new key format (e.g., adding an optional third part to the keys processed by +`TwoPartComparator`), (a) keep the same comparator name (b) increment the +version number for new keys (c) change the comparator function so it uses the +version numbers found in the keys to decide how to interpret them. + +## Performance + +Performance can be tuned by changing the default values of the types defined in +`include/leveldb/options.h`. + +### Block size + +leveldb groups adjacent keys together into the same block and such a block is +the unit of transfer to and from persistent storage. The default block size is +approximately 4096 uncompressed bytes. Applications that mostly do bulk scans +over the contents of the database may wish to increase this size. Applications +that do a lot of point reads of small values may wish to switch to a smaller +block size if performance measurements indicate an improvement. There isn't much +benefit in using blocks smaller than one kilobyte, or larger than a few +megabytes. Also note that compression will be more effective with larger block +sizes. + +### Compression + +Each block is individually compressed before being written to persistent +storage. Compression is on by default since the default compression method is +very fast, and is automatically disabled for uncompressible data. In rare cases, +applications may want to disable compression entirely, but should only do so if +benchmarks show a performance improvement: + +```c++ +leveldb::Options options; +options.compression = leveldb::kNoCompression; +... leveldb::DB::Open(options, name, ...) .... +``` + +### Cache + +The contents of the database are stored in a set of files in the filesystem and +each file stores a sequence of compressed blocks. If options.cache is non-NULL, +it is used to cache frequently used uncompressed block contents. + +```c++ +#include "leveldb/cache.h" + +leveldb::Options options; +options.cache = leveldb::NewLRUCache(100 * 1048576); // 100MB cache +leveldb::DB* db; +leveldb::DB::Open(options, name, &db); +... use the db ... +delete db +delete options.cache; +``` + +Note that the cache holds uncompressed data, and therefore it should be sized +according to application level data sizes, without any reduction from +compression. (Caching of compressed blocks is left to the operating system +buffer cache, or any custom Env implementation provided by the client.) + +When performing a bulk read, the application may wish to disable caching so that +the data processed by the bulk read does not end up displacing most of the +cached contents. A per-iterator option can be used to achieve this: + +```c++ +leveldb::ReadOptions options; +options.fill_cache = false; +leveldb::Iterator* it = db->NewIterator(options); +for (it->SeekToFirst(); it->Valid(); it->Next()) { + ... +} +``` + +### Key Layout + +Note that the unit of disk transfer and caching is a block. Adjacent keys +(according to the database sort order) will usually be placed in the same block. +Therefore the application can improve its performance by placing keys that are +accessed together near each other and placing infrequently used keys in a +separate region of the key space. + +For example, suppose we are implementing a simple file system on top of leveldb. +The types of entries we might wish to store are: + + filename -> permission-bits, length, list of file_block_ids + file_block_id -> data + +We might want to prefix filename keys with one letter (say '/') and the +`file_block_id` keys with a different letter (say '0') so that scans over just +the metadata do not force us to fetch and cache bulky file contents. + +### Filters + +Because of the way leveldb data is organized on disk, a single `Get()` call may +involve multiple reads from disk. The optional FilterPolicy mechanism can be +used to reduce the number of disk reads substantially. + +```c++ +leveldb::Options options; +options.filter_policy = NewBloomFilterPolicy(10); +leveldb::DB* db; +leveldb::DB::Open(options, "/tmp/testdb", &db); +... use the database ... +delete db; +delete options.filter_policy; +``` + +The preceding code associates a Bloom filter based filtering policy with the +database. Bloom filter based filtering relies on keeping some number of bits of +data in memory per key (in this case 10 bits per key since that is the argument +we passed to `NewBloomFilterPolicy`). This filter will reduce the number of +unnecessary disk reads needed for Get() calls by a factor of approximately +a 100. Increasing the bits per key will lead to a larger reduction at the cost +of more memory usage. We recommend that applications whose working set does not +fit in memory and that do a lot of random reads set a filter policy. + +If you are using a custom comparator, you should ensure that the filter policy +you are using is compatible with your comparator. For example, consider a +comparator that ignores trailing spaces when comparing keys. +`NewBloomFilterPolicy` must not be used with such a comparator. Instead, the +application should provide a custom filter policy that also ignores trailing +spaces. For example: + +```c++ +class CustomFilterPolicy : public leveldb::FilterPolicy { + private: + FilterPolicy* builtin_policy_; + + public: + CustomFilterPolicy() : builtin_policy_(NewBloomFilterPolicy(10)) {} + ~CustomFilterPolicy() { delete builtin_policy_; } + + const char* Name() const { return "IgnoreTrailingSpacesFilter"; } + + void CreateFilter(const Slice* keys, int n, std::string* dst) const { + // Use builtin bloom filter code after removing trailing spaces + std::vector trimmed(n); + for (int i = 0; i < n; i++) { + trimmed[i] = RemoveTrailingSpaces(keys[i]); + } + return builtin_policy_->CreateFilter(&trimmed[i], n, dst); + } +}; +``` + +Advanced applications may provide a filter policy that does not use a bloom +filter but uses some other mechanism for summarizing a set of keys. See +`leveldb/filter_policy.h` for detail. + +## Checksums + +leveldb associates checksums with all data it stores in the file system. There +are two separate controls provided over how aggressively these checksums are +verified: + +`ReadOptions::verify_checksums` may be set to true to force checksum +verification of all data that is read from the file system on behalf of a +particular read. By default, no such verification is done. + +`Options::paranoid_checks` may be set to true before opening a database to make +the database implementation raise an error as soon as it detects an internal +corruption. Depending on which portion of the database has been corrupted, the +error may be raised when the database is opened, or later by another database +operation. By default, paranoid checking is off so that the database can be used +even if parts of its persistent storage have been corrupted. + +If a database is corrupted (perhaps it cannot be opened when paranoid checking +is turned on), the `leveldb::RepairDB` function may be used to recover as much +of the data as possible + +## Approximate Sizes + +The `GetApproximateSizes` method can used to get the approximate number of bytes +of file system space used by one or more key ranges. + +```c++ +leveldb::Range ranges[2]; +ranges[0] = leveldb::Range("a", "c"); +ranges[1] = leveldb::Range("x", "z"); +uint64_t sizes[2]; +leveldb::Status s = db->GetApproximateSizes(ranges, 2, sizes); +``` + +The preceding call will set `sizes[0]` to the approximate number of bytes of +file system space used by the key range `[a..c)` and `sizes[1]` to the +approximate number of bytes used by the key range `[x..z)`. + +## Environment + +All file operations (and other operating system calls) issued by the leveldb +implementation are routed through a `leveldb::Env` object. Sophisticated clients +may wish to provide their own Env implementation to get better control. +For example, an application may introduce artificial delays in the file IO +paths to limit the impact of leveldb on other activities in the system. + +```c++ +class SlowEnv : public leveldb::Env { + ... implementation of the Env interface ... +}; + +SlowEnv env; +leveldb::Options options; +options.env = &env; +Status s = leveldb::DB::Open(options, ...); +``` + +## Porting + +leveldb may be ported to a new platform by providing platform specific +implementations of the types/methods/functions exported by +`leveldb/port/port.h`. See `leveldb/port/port_example.h` for more details. + +In addition, the new platform may need a new default `leveldb::Env` +implementation. See `leveldb/util/env_posix.h` for an example. + +## Other Information + +Details about the leveldb implementation may be found in the following +documents: + +1. [Implementation notes](impl.md) +2. [Format of an immutable Table file](table_format.md) +3. [Format of a log file](log_format.md) diff --git a/src/leveldb/doc/log_format.md b/src/leveldb/doc/log_format.md new file mode 100644 index 00000000..f32cb5d7 --- /dev/null +++ b/src/leveldb/doc/log_format.md @@ -0,0 +1,75 @@ +leveldb Log format +================== +The log file contents are a sequence of 32KB blocks. The only exception is that +the tail of the file may contain a partial block. + +Each block consists of a sequence of records: + + block := record* trailer? + record := + checksum: uint32 // crc32c of type and data[] ; little-endian + length: uint16 // little-endian + type: uint8 // One of FULL, FIRST, MIDDLE, LAST + data: uint8[length] + +A record never starts within the last six bytes of a block (since it won't fit). +Any leftover bytes here form the trailer, which must consist entirely of zero +bytes and must be skipped by readers. + +Aside: if exactly seven bytes are left in the current block, and a new non-zero +length record is added, the writer must emit a FIRST record (which contains zero +bytes of user data) to fill up the trailing seven bytes of the block and then +emit all of the user data in subsequent blocks. + +More types may be added in the future. Some Readers may skip record types they +do not understand, others may report that some data was skipped. + + FULL == 1 + FIRST == 2 + MIDDLE == 3 + LAST == 4 + +The FULL record contains the contents of an entire user record. + +FIRST, MIDDLE, LAST are types used for user records that have been split into +multiple fragments (typically because of block boundaries). FIRST is the type +of the first fragment of a user record, LAST is the type of the last fragment of +a user record, and MIDDLE is the type of all interior fragments of a user +record. + +Example: consider a sequence of user records: + + A: length 1000 + B: length 97270 + C: length 8000 + +**A** will be stored as a FULL record in the first block. + +**B** will be split into three fragments: first fragment occupies the rest of +the first block, second fragment occupies the entirety of the second block, and +the third fragment occupies a prefix of the third block. This will leave six +bytes free in the third block, which will be left empty as the trailer. + +**C** will be stored as a FULL record in the fourth block. + +---- + +## Some benefits over the recordio format: + +1. We do not need any heuristics for resyncing - just go to next block boundary + and scan. If there is a corruption, skip to the next block. As a + side-benefit, we do not get confused when part of the contents of one log + file are embedded as a record inside another log file. + +2. Splitting at approximate boundaries (e.g., for mapreduce) is simple: find the + next block boundary and skip records until we hit a FULL or FIRST record. + +3. We do not need extra buffering for large records. + +## Some downsides compared to recordio format: + +1. No packing of tiny records. This could be fixed by adding a new record type, + so it is a shortcoming of the current implementation, not necessarily the + format. + +2. No compression. Again, this could be fixed by adding new record types. diff --git a/src/leveldb/doc/log_format.txt b/src/leveldb/doc/log_format.txt deleted file mode 100644 index 4cca5ef6..00000000 --- a/src/leveldb/doc/log_format.txt +++ /dev/null @@ -1,75 +0,0 @@ -The log file contents are a sequence of 32KB blocks. The only -exception is that the tail of the file may contain a partial block. - -Each block consists of a sequence of records: - block := record* trailer? - record := - checksum: uint32 // crc32c of type and data[] ; little-endian - length: uint16 // little-endian - type: uint8 // One of FULL, FIRST, MIDDLE, LAST - data: uint8[length] - -A record never starts within the last six bytes of a block (since it -won't fit). Any leftover bytes here form the trailer, which must -consist entirely of zero bytes and must be skipped by readers. - -Aside: if exactly seven bytes are left in the current block, and a new -non-zero length record is added, the writer must emit a FIRST record -(which contains zero bytes of user data) to fill up the trailing seven -bytes of the block and then emit all of the user data in subsequent -blocks. - -More types may be added in the future. Some Readers may skip record -types they do not understand, others may report that some data was -skipped. - -FULL == 1 -FIRST == 2 -MIDDLE == 3 -LAST == 4 - -The FULL record contains the contents of an entire user record. - -FIRST, MIDDLE, LAST are types used for user records that have been -split into multiple fragments (typically because of block boundaries). -FIRST is the type of the first fragment of a user record, LAST is the -type of the last fragment of a user record, and MIDDLE is the type of -all interior fragments of a user record. - -Example: consider a sequence of user records: - A: length 1000 - B: length 97270 - C: length 8000 -A will be stored as a FULL record in the first block. - -B will be split into three fragments: first fragment occupies the rest -of the first block, second fragment occupies the entirety of the -second block, and the third fragment occupies a prefix of the third -block. This will leave six bytes free in the third block, which will -be left empty as the trailer. - -C will be stored as a FULL record in the fourth block. - -=================== - -Some benefits over the recordio format: - -(1) We do not need any heuristics for resyncing - just go to next -block boundary and scan. If there is a corruption, skip to the next -block. As a side-benefit, we do not get confused when part of the -contents of one log file are embedded as a record inside another log -file. - -(2) Splitting at approximate boundaries (e.g., for mapreduce) is -simple: find the next block boundary and skip records until we -hit a FULL or FIRST record. - -(3) We do not need extra buffering for large records. - -Some downsides compared to recordio format: - -(1) No packing of tiny records. This could be fixed by adding a new -record type, so it is a shortcoming of the current implementation, -not necessarily the format. - -(2) No compression. Again, this could be fixed by adding new record types. diff --git a/src/leveldb/doc/table_format.md b/src/leveldb/doc/table_format.md new file mode 100644 index 00000000..5fe7e724 --- /dev/null +++ b/src/leveldb/doc/table_format.md @@ -0,0 +1,107 @@ +leveldb File format +=================== + + + [data block 1] + [data block 2] + ... + [data block N] + [meta block 1] + ... + [meta block K] + [metaindex block] + [index block] + [Footer] (fixed size; starts at file_size - sizeof(Footer)) + + +The file contains internal pointers. Each such pointer is called +a BlockHandle and contains the following information: + + offset: varint64 + size: varint64 + +See [varints](https://developers.google.com/protocol-buffers/docs/encoding#varints) +for an explanation of varint64 format. + +1. The sequence of key/value pairs in the file are stored in sorted +order and partitioned into a sequence of data blocks. These blocks +come one after another at the beginning of the file. Each data block +is formatted according to the code in `block_builder.cc`, and then +optionally compressed. + +2. After the data blocks we store a bunch of meta blocks. The +supported meta block types are described below. More meta block types +may be added in the future. Each meta block is again formatted using +`block_builder.cc` and then optionally compressed. + +3. A "metaindex" block. It contains one entry for every other meta +block where the key is the name of the meta block and the value is a +BlockHandle pointing to that meta block. + +4. An "index" block. This block contains one entry per data block, +where the key is a string >= last key in that data block and before +the first key in the successive data block. The value is the +BlockHandle for the data block. + +5. At the very end of the file is a fixed length footer that contains +the BlockHandle of the metaindex and index blocks as well as a magic number. + + metaindex_handle: char[p]; // Block handle for metaindex + index_handle: char[q]; // Block handle for index + padding: char[40-p-q];// zeroed bytes to make fixed length + // (40==2*BlockHandle::kMaxEncodedLength) + magic: fixed64; // == 0xdb4775248b80fb57 (little-endian) + +## "filter" Meta Block + +If a `FilterPolicy` was specified when the database was opened, a +filter block is stored in each table. The "metaindex" block contains +an entry that maps from `filter.` to the BlockHandle for the filter +block where `` is the string returned by the filter policy's +`Name()` method. + +The filter block stores a sequence of filters, where filter i contains +the output of `FilterPolicy::CreateFilter()` on all keys that are stored +in a block whose file offset falls within the range + + [ i*base ... (i+1)*base-1 ] + +Currently, "base" is 2KB. So for example, if blocks X and Y start in +the range `[ 0KB .. 2KB-1 ]`, all of the keys in X and Y will be +converted to a filter by calling `FilterPolicy::CreateFilter()`, and the +resulting filter will be stored as the first filter in the filter +block. + +The filter block is formatted as follows: + + [filter 0] + [filter 1] + [filter 2] + ... + [filter N-1] + + [offset of filter 0] : 4 bytes + [offset of filter 1] : 4 bytes + [offset of filter 2] : 4 bytes + ... + [offset of filter N-1] : 4 bytes + + [offset of beginning of offset array] : 4 bytes + lg(base) : 1 byte + +The offset array at the end of the filter block allows efficient +mapping from a data block offset to the corresponding filter. + +## "stats" Meta Block + +This meta block contains a bunch of stats. The key is the name +of the statistic. The value contains the statistic. + +TODO(postrelease): record following stats. + + data size + index size + key size (uncompressed) + value size (uncompressed) + number of entries + number of data blocks diff --git a/src/leveldb/doc/table_format.txt b/src/leveldb/doc/table_format.txt deleted file mode 100644 index ca8f9b44..00000000 --- a/src/leveldb/doc/table_format.txt +++ /dev/null @@ -1,104 +0,0 @@ -File format -=========== - - - [data block 1] - [data block 2] - ... - [data block N] - [meta block 1] - ... - [meta block K] - [metaindex block] - [index block] - [Footer] (fixed size; starts at file_size - sizeof(Footer)) - - -The file contains internal pointers. Each such pointer is called -a BlockHandle and contains the following information: - offset: varint64 - size: varint64 -See https://developers.google.com/protocol-buffers/docs/encoding#varints -for an explanation of varint64 format. - -(1) The sequence of key/value pairs in the file are stored in sorted -order and partitioned into a sequence of data blocks. These blocks -come one after another at the beginning of the file. Each data block -is formatted according to the code in block_builder.cc, and then -optionally compressed. - -(2) After the data blocks we store a bunch of meta blocks. The -supported meta block types are described below. More meta block types -may be added in the future. Each meta block is again formatted using -block_builder.cc and then optionally compressed. - -(3) A "metaindex" block. It contains one entry for every other meta -block where the key is the name of the meta block and the value is a -BlockHandle pointing to that meta block. - -(4) An "index" block. This block contains one entry per data block, -where the key is a string >= last key in that data block and before -the first key in the successive data block. The value is the -BlockHandle for the data block. - -(6) At the very end of the file is a fixed length footer that contains -the BlockHandle of the metaindex and index blocks as well as a magic number. - metaindex_handle: char[p]; // Block handle for metaindex - index_handle: char[q]; // Block handle for index - padding: char[40-p-q]; // zeroed bytes to make fixed length - // (40==2*BlockHandle::kMaxEncodedLength) - magic: fixed64; // == 0xdb4775248b80fb57 (little-endian) - -"filter" Meta Block -------------------- - -If a "FilterPolicy" was specified when the database was opened, a -filter block is stored in each table. The "metaindex" block contains -an entry that maps from "filter." to the BlockHandle for the filter -block where "" is the string returned by the filter policy's -"Name()" method. - -The filter block stores a sequence of filters, where filter i contains -the output of FilterPolicy::CreateFilter() on all keys that are stored -in a block whose file offset falls within the range - - [ i*base ... (i+1)*base-1 ] - -Currently, "base" is 2KB. So for example, if blocks X and Y start in -the range [ 0KB .. 2KB-1 ], all of the keys in X and Y will be -converted to a filter by calling FilterPolicy::CreateFilter(), and the -resulting filter will be stored as the first filter in the filter -block. - -The filter block is formatted as follows: - - [filter 0] - [filter 1] - [filter 2] - ... - [filter N-1] - - [offset of filter 0] : 4 bytes - [offset of filter 1] : 4 bytes - [offset of filter 2] : 4 bytes - ... - [offset of filter N-1] : 4 bytes - - [offset of beginning of offset array] : 4 bytes - lg(base) : 1 byte - -The offset array at the end of the filter block allows efficient -mapping from a data block offset to the corresponding filter. - -"stats" Meta Block ------------------- - -This meta block contains a bunch of stats. The key is the name -of the statistic. The value contains the statistic. -TODO(postrelease): record following stats. - data size - index size - key size (uncompressed) - value size (uncompressed) - number of entries - number of data blocks diff --git a/src/leveldb/helpers/memenv/memenv.cc b/src/leveldb/helpers/memenv/memenv.cc index 43ef2e07..68c0614a 100644 --- a/src/leveldb/helpers/memenv/memenv.cc +++ b/src/leveldb/helpers/memenv/memenv.cc @@ -176,6 +176,7 @@ class SequentialFileImpl : public SequentialFile { return Status::OK(); } + virtual std::string GetName() const { return "[memenv]"; } private: FileState* file_; uint64_t pos_; @@ -196,6 +197,7 @@ class RandomAccessFileImpl : public RandomAccessFile { return file_->Read(offset, n, result, scratch); } + virtual std::string GetName() const { return "[memenv]"; } private: FileState* file_; }; @@ -218,6 +220,7 @@ class WritableFileImpl : public WritableFile { virtual Status Flush() { return Status::OK(); } virtual Status Sync() { return Status::OK(); } + virtual std::string GetName() const { return "[memenv]"; } private: FileState* file_; }; @@ -277,6 +280,19 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result) { + MutexLock lock(&mutex_); + FileState** sptr = &file_map_[fname]; + FileState* file = *sptr; + if (file == NULL) { + file = new FileState(); + file->Ref(); + } + *result = new WritableFileImpl(file); + return Status::OK(); + } + virtual bool FileExists(const std::string& fname) { MutexLock lock(&mutex_); return file_map_.find(fname) != file_map_.end(); diff --git a/src/leveldb/helpers/memenv/memenv_test.cc b/src/leveldb/helpers/memenv/memenv_test.cc index a44310fe..5cff7761 100644 --- a/src/leveldb/helpers/memenv/memenv_test.cc +++ b/src/leveldb/helpers/memenv/memenv_test.cc @@ -40,6 +40,8 @@ TEST(MemEnvTest, Basics) { // Create a file. ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(0, file_size); delete writable_file; // Check that the file exists. @@ -55,9 +57,16 @@ TEST(MemEnvTest, Basics) { ASSERT_OK(writable_file->Append("abc")); delete writable_file; - // Check for expected size. + // Check that append works. + ASSERT_OK(env_->NewAppendableFile("/dir/f", &writable_file)); ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); ASSERT_EQ(3, file_size); + ASSERT_OK(writable_file->Append("hello")); + delete writable_file; + + // Check for expected size. + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(8, file_size); // Check that renaming works. ASSERT_TRUE(!env_->RenameFile("/dir/non_existent", "/dir/g").ok()); @@ -65,7 +74,7 @@ TEST(MemEnvTest, Basics) { ASSERT_TRUE(!env_->FileExists("/dir/f")); ASSERT_TRUE(env_->FileExists("/dir/g")); ASSERT_OK(env_->GetFileSize("/dir/g", &file_size)); - ASSERT_EQ(3, file_size); + ASSERT_EQ(8, file_size); // Check that opening non-existent file fails. SequentialFile* seq_file; diff --git a/src/leveldb/include/leveldb/cache.h b/src/leveldb/include/leveldb/cache.h index 1a201e5e..6819d5bc 100644 --- a/src/leveldb/include/leveldb/cache.h +++ b/src/leveldb/include/leveldb/cache.h @@ -81,6 +81,17 @@ class Cache { // its cache keys. virtual uint64_t NewId() = 0; + // Remove all cache entries that are not actively in use. Memory-constrained + // applications may wish to call this method to reduce memory usage. + // Default implementation of Prune() does nothing. Subclasses are strongly + // encouraged to override the default implementation. A future release of + // leveldb may change Prune() to a pure abstract method. + virtual void Prune() {} + + // Return an estimate of the combined charges of all elements stored in the + // cache. + virtual size_t TotalCharge() const = 0; + private: void LRU_Remove(Handle* e); void LRU_Append(Handle* e); diff --git a/src/leveldb/include/leveldb/db.h b/src/leveldb/include/leveldb/db.h index 4c169bf2..bfab10a0 100644 --- a/src/leveldb/include/leveldb/db.h +++ b/src/leveldb/include/leveldb/db.h @@ -14,7 +14,7 @@ namespace leveldb { // Update Makefile if you change these static const int kMajorVersion = 1; -static const int kMinorVersion = 18; +static const int kMinorVersion = 20; struct Options; struct ReadOptions; @@ -115,6 +115,8 @@ class DB { // about the internal operation of the DB. // "leveldb.sstables" - returns a multi-line string that describes all // of the sstables that make up the db contents. + // "leveldb.approximate-memory-usage" - returns the approximate number of + // bytes of memory in use by the DB. virtual bool GetProperty(const Slice& property, std::string* value) = 0; // For each i in [0,n-1], store in "sizes[i]", the approximate diff --git a/src/leveldb/include/leveldb/env.h b/src/leveldb/include/leveldb/env.h index f709514d..275d441e 100644 --- a/src/leveldb/include/leveldb/env.h +++ b/src/leveldb/include/leveldb/env.h @@ -69,6 +69,21 @@ class Env { virtual Status NewWritableFile(const std::string& fname, WritableFile** result) = 0; + // Create an object that either appends to an existing file, or + // writes to a new file (if the file does not exist to begin with). + // On success, stores a pointer to the new file in *result and + // returns OK. On failure stores NULL in *result and returns + // non-OK. + // + // The returned file will only be accessed by one thread at a time. + // + // May return an IsNotSupportedError error if this Env does + // not allow appending to an existing file. Users of Env (including + // the leveldb implementation) must be prepared to deal with + // an Env that does not support appending. + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result); + // Returns true iff the named file exists. virtual bool FileExists(const std::string& fname) = 0; @@ -176,6 +191,9 @@ class SequentialFile { // REQUIRES: External synchronization virtual Status Skip(uint64_t n) = 0; + // Get a name for the file, only for error reporting + virtual std::string GetName() const = 0; + private: // No copying allowed SequentialFile(const SequentialFile&); @@ -200,6 +218,9 @@ class RandomAccessFile { virtual Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const = 0; + // Get a name for the file, only for error reporting + virtual std::string GetName() const = 0; + private: // No copying allowed RandomAccessFile(const RandomAccessFile&); @@ -219,6 +240,9 @@ class WritableFile { virtual Status Flush() = 0; virtual Status Sync() = 0; + // Get a name for the file, only for error reporting + virtual std::string GetName() const = 0; + private: // No copying allowed WritableFile(const WritableFile&); @@ -289,6 +313,9 @@ class EnvWrapper : public Env { Status NewWritableFile(const std::string& f, WritableFile** r) { return target_->NewWritableFile(f, r); } + Status NewAppendableFile(const std::string& f, WritableFile** r) { + return target_->NewAppendableFile(f, r); + } bool FileExists(const std::string& f) { return target_->FileExists(f); } Status GetChildren(const std::string& dir, std::vector* r) { return target_->GetChildren(dir, r); diff --git a/src/leveldb/include/leveldb/iterator.h b/src/leveldb/include/leveldb/iterator.h index 76aced04..da631ed9 100644 --- a/src/leveldb/include/leveldb/iterator.h +++ b/src/leveldb/include/leveldb/iterator.h @@ -37,7 +37,7 @@ class Iterator { // Valid() after this call iff the source is not empty. virtual void SeekToLast() = 0; - // Position at the first key in the source that at or past target + // Position at the first key in the source that is at or past target. // The iterator is Valid() after this call iff the source contains // an entry that comes at or past target. virtual void Seek(const Slice& target) = 0; diff --git a/src/leveldb/include/leveldb/options.h b/src/leveldb/include/leveldb/options.h index 7c9b9734..976e3812 100644 --- a/src/leveldb/include/leveldb/options.h +++ b/src/leveldb/include/leveldb/options.h @@ -112,6 +112,18 @@ struct Options { // Default: 16 int block_restart_interval; + // Leveldb will write up to this amount of bytes to a file before + // switching to a new one. + // Most clients should leave this parameter alone. However if your + // filesystem is more efficient with larger files, you could + // consider increasing the value. The downside will be longer + // compactions and hence longer latency/performance hiccups. + // Another reason to increase this parameter might be when you are + // initially populating a large database. + // + // Default: 2MB + size_t max_file_size; + // Compress blocks using the specified compression algorithm. This // parameter can be changed dynamically. // @@ -128,6 +140,12 @@ struct Options { // efficiently detect that and will switch to uncompressed mode. CompressionType compression; + // EXPERIMENTAL: If true, append to existing MANIFEST and log files + // when a database is opened. This can significantly speed up open. + // + // Default: currently false, but may become true later. + bool reuse_logs; + // If non-NULL, use the specified filter policy to reduce disk reads. // Many applications will benefit from passing the result of // NewBloomFilterPolicy() here. diff --git a/src/leveldb/include/leveldb/status.h b/src/leveldb/include/leveldb/status.h index 11dbd4b4..d9575f97 100644 --- a/src/leveldb/include/leveldb/status.h +++ b/src/leveldb/include/leveldb/status.h @@ -60,6 +60,12 @@ class Status { // Returns true iff the status indicates an IOError. bool IsIOError() const { return code() == kIOError; } + // Returns true iff the status indicates a NotSupportedError. + bool IsNotSupportedError() const { return code() == kNotSupported; } + + // Returns true iff the status indicates an InvalidArgument. + bool IsInvalidArgument() const { return code() == kInvalidArgument; } + // Return a string representation of this status suitable for printing. // Returns the string "OK" for success. std::string ToString() const; diff --git a/src/leveldb/port/atomic_pointer.h b/src/leveldb/port/atomic_pointer.h index 9bf091f7..d79a0223 100644 --- a/src/leveldb/port/atomic_pointer.h +++ b/src/leveldb/port/atomic_pointer.h @@ -35,13 +35,41 @@ #define ARCH_CPU_X86_FAMILY 1 #elif defined(__ARMEL__) #define ARCH_CPU_ARM_FAMILY 1 +#elif defined(__aarch64__) +#define ARCH_CPU_ARM64_FAMILY 1 #elif defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__) #define ARCH_CPU_PPC_FAMILY 1 +#elif defined(__mips__) +#define ARCH_CPU_MIPS_FAMILY 1 #endif namespace leveldb { namespace port { +// AtomicPointer based on if available +#if defined(LEVELDB_ATOMIC_PRESENT) +class AtomicPointer { + private: + std::atomic rep_; + public: + AtomicPointer() { } + explicit AtomicPointer(void* v) : rep_(v) { } + inline void* Acquire_Load() const { + return rep_.load(std::memory_order_acquire); + } + inline void Release_Store(void* v) { + rep_.store(v, std::memory_order_release); + } + inline void* NoBarrier_Load() const { + return rep_.load(std::memory_order_relaxed); + } + inline void NoBarrier_Store(void* v) { + rep_.store(v, std::memory_order_relaxed); + } +}; + +#else + // Define MemoryBarrier() if available // Windows on x86 #if defined(OS_WIN) && defined(COMPILER_MSVC) && defined(ARCH_CPU_X86_FAMILY) @@ -92,6 +120,13 @@ inline void MemoryBarrier() { } #define LEVELDB_HAVE_MEMORY_BARRIER +// ARM64 +#elif defined(ARCH_CPU_ARM64_FAMILY) +inline void MemoryBarrier() { + asm volatile("dmb sy" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + // PPC #elif defined(ARCH_CPU_PPC_FAMILY) && defined(__GNUC__) inline void MemoryBarrier() { @@ -101,6 +136,13 @@ inline void MemoryBarrier() { } #define LEVELDB_HAVE_MEMORY_BARRIER +// MIPS +#elif defined(ARCH_CPU_MIPS_FAMILY) && defined(__GNUC__) +inline void MemoryBarrier() { + __asm__ __volatile__("sync" : : : "memory"); +} +#define LEVELDB_HAVE_MEMORY_BARRIER + #endif // AtomicPointer built using platform-specific MemoryBarrier() @@ -124,28 +166,6 @@ class AtomicPointer { } }; -// AtomicPointer based on -#elif defined(LEVELDB_ATOMIC_PRESENT) -class AtomicPointer { - private: - std::atomic rep_; - public: - AtomicPointer() { } - explicit AtomicPointer(void* v) : rep_(v) { } - inline void* Acquire_Load() const { - return rep_.load(std::memory_order_acquire); - } - inline void Release_Store(void* v) { - rep_.store(v, std::memory_order_release); - } - inline void* NoBarrier_Load() const { - return rep_.load(std::memory_order_relaxed); - } - inline void NoBarrier_Store(void* v) { - rep_.store(v, std::memory_order_relaxed); - } -}; - // Atomic pointer based on sparc memory barriers #elif defined(__sparcv9) && defined(__GNUC__) class AtomicPointer { @@ -210,11 +230,13 @@ class AtomicPointer { #else #error Please implement AtomicPointer for this platform. +#endif #endif #undef LEVELDB_HAVE_MEMORY_BARRIER #undef ARCH_CPU_X86_FAMILY #undef ARCH_CPU_ARM_FAMILY +#undef ARCH_CPU_ARM64_FAMILY #undef ARCH_CPU_PPC_FAMILY } // namespace port diff --git a/src/leveldb/port/port_example.h b/src/leveldb/port/port_example.h index ab9e489b..5b1d027d 100644 --- a/src/leveldb/port/port_example.h +++ b/src/leveldb/port/port_example.h @@ -129,6 +129,16 @@ extern bool Snappy_Uncompress(const char* input_data, size_t input_length, // The concatenation of all "data[0,n-1]" fragments is the heap profile. extern bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg); +// Determine whether a working accelerated crc32 implementation exists +// Returns true if AcceleratedCRC32C is safe to call +bool HasAcceleratedCRC32C(); + +// Extend the CRC to include the first n bytes of buf. +// +// Returns zero if the CRC cannot be extended using acceleration, else returns +// the newly extended CRC value (which may also be zero). +uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size); + } // namespace port } // namespace leveldb diff --git a/src/leveldb/port/port_posix.cc b/src/leveldb/port/port_posix.cc index 5ba127a5..ec39e921 100644 --- a/src/leveldb/port/port_posix.cc +++ b/src/leveldb/port/port_posix.cc @@ -7,7 +7,10 @@ #include #include #include -#include "util/logging.h" + +#if (defined(__x86_64__) || defined(__i386__)) && defined(__GNUC__) +#include +#endif namespace leveldb { namespace port { @@ -50,5 +53,15 @@ void InitOnce(OnceType* once, void (*initializer)()) { PthreadCall("once", pthread_once(once, initializer)); } +bool HasAcceleratedCRC32C() { +#if (defined(__x86_64__) || defined(__i386__)) && defined(__GNUC__) + unsigned int eax, ebx, ecx, edx; + __get_cpuid(1, &eax, &ebx, &ecx, &edx); + return (ecx & (1 << 20)) != 0; +#else + return false; +#endif +} + } // namespace port } // namespace leveldb diff --git a/src/leveldb/port/port_posix.h b/src/leveldb/port/port_posix.h index ccca9939..d85fa5d6 100644 --- a/src/leveldb/port/port_posix.h +++ b/src/leveldb/port/port_posix.h @@ -152,6 +152,9 @@ inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { return false; } +bool HasAcceleratedCRC32C(); +uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size); + } // namespace port } // namespace leveldb diff --git a/src/leveldb/port/port_posix_sse.cc b/src/leveldb/port/port_posix_sse.cc new file mode 100644 index 00000000..2d49c21d --- /dev/null +++ b/src/leveldb/port/port_posix_sse.cc @@ -0,0 +1,110 @@ +// Copyright 2016 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// A portable implementation of crc32c, optimized to handle +// four bytes at a time. +// +// In a separate source file to allow this accelerated CRC32C function to be +// compiled with the appropriate compiler flags to enable x86 SSE 4.2 +// instructions. + +#include +#include +#include "port/port.h" + +#if defined(LEVELDB_PLATFORM_POSIX_SSE) + +#if defined(_MSC_VER) +#include +#elif defined(__GNUC__) && defined(__SSE4_2__) +#include +#endif + +#endif // defined(LEVELDB_PLATFORM_POSIX_SSE) + +namespace leveldb { +namespace port { + +#if defined(LEVELDB_PLATFORM_POSIX_SSE) + +// Used to fetch a naturally-aligned 32-bit word in little endian byte-order +static inline uint32_t LE_LOAD32(const uint8_t *p) { + // SSE is x86 only, so ensured that |p| is always little-endian. + uint32_t word; + memcpy(&word, p, sizeof(word)); + return word; +} + +#if defined(_M_X64) || defined(__x86_64__) // LE_LOAD64 is only used on x64. + +// Used to fetch a naturally-aligned 64-bit word in little endian byte-order +static inline uint64_t LE_LOAD64(const uint8_t *p) { + uint64_t dword; + memcpy(&dword, p, sizeof(dword)); + return dword; +} + +#endif // defined(_M_X64) || defined(__x86_64__) + +#endif // defined(LEVELDB_PLATFORM_POSIX_SSE) + +// For further improvements see Intel publication at: +// http://download.intel.com/design/intarch/papers/323405.pdf +uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size) { +#if !defined(LEVELDB_PLATFORM_POSIX_SSE) + return 0; +#else + + const uint8_t *p = reinterpret_cast(buf); + const uint8_t *e = p + size; + uint32_t l = crc ^ 0xffffffffu; + +#define STEP1 do { \ + l = _mm_crc32_u8(l, *p++); \ +} while (0) +#define STEP4 do { \ + l = _mm_crc32_u32(l, LE_LOAD32(p)); \ + p += 4; \ +} while (0) +#define STEP8 do { \ + l = _mm_crc32_u64(l, LE_LOAD64(p)); \ + p += 8; \ +} while (0) + + if (size > 16) { + // Process unaligned bytes + for (unsigned int i = reinterpret_cast(p) % 8; i; --i) { + STEP1; + } + + // _mm_crc32_u64 is only available on x64. +#if defined(_M_X64) || defined(__x86_64__) + // Process 8 bytes at a time + while ((e-p) >= 8) { + STEP8; + } + // Process 4 bytes at a time + if ((e-p) >= 4) { + STEP4; + } +#else // !(defined(_M_X64) || defined(__x86_64__)) + // Process 4 bytes at a time + while ((e-p) >= 4) { + STEP4; + } +#endif // defined(_M_X64) || defined(__x86_64__) + } + // Process the last few bytes + while (p != e) { + STEP1; + } +#undef STEP8 +#undef STEP4 +#undef STEP1 + return l ^ 0xffffffffu; +#endif // defined(LEVELDB_PLATFORM_POSIX_SSE) +} + +} // namespace port +} // namespace leveldb diff --git a/src/leveldb/port/port_win.cc b/src/leveldb/port/port_win.cc index 1b0f060a..1be9e8d5 100644 --- a/src/leveldb/port/port_win.cc +++ b/src/leveldb/port/port_win.cc @@ -32,6 +32,7 @@ #include #include +#include namespace leveldb { namespace port { @@ -143,5 +144,15 @@ void AtomicPointer::NoBarrier_Store(void* v) { rep_ = v; } +bool HasAcceleratedCRC32C() { +#if defined(__x86_64__) || defined(__i386__) + int cpu_info[4]; + __cpuid(cpu_info, 1); + return (cpu_info[2] & (1 << 20)) != 0; +#else + return false; +#endif +} + } } diff --git a/src/leveldb/port/port_win.h b/src/leveldb/port/port_win.h index 45bf2f0e..ba7b17fa 100644 --- a/src/leveldb/port/port_win.h +++ b/src/leveldb/port/port_win.h @@ -32,13 +32,21 @@ #define STORAGE_LEVELDB_PORT_PORT_WIN_H_ #ifdef _MSC_VER +#if !(_MSC_VER >= 1900) #define snprintf _snprintf +#endif #define close _close #define fread_unlocked _fread_nolock +#ifdef _WIN64 +#define ssize_t int64_t +#else +#define ssize_t int32_t +#endif #endif #include #include +static_assert(sizeof(ssize_t) == sizeof(size_t), "ssize_t should be the same size as size_t"); #ifdef SNAPPY #include #endif @@ -168,6 +176,9 @@ inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { return false; } +bool HasAcceleratedCRC32C(); +uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size); + } } diff --git a/src/leveldb/table/filter_block.cc b/src/leveldb/table/filter_block.cc index 203e15c8..1ed51341 100644 --- a/src/leveldb/table/filter_block.cc +++ b/src/leveldb/table/filter_block.cc @@ -9,7 +9,7 @@ namespace leveldb { -// See doc/table_format.txt for an explanation of the filter block format. +// See doc/table_format.md for an explanation of the filter block format. // Generate new filter every 2KB of data static const size_t kFilterBaseLg = 11; @@ -68,7 +68,7 @@ void FilterBlockBuilder::GenerateFilter() { // Generate filter for current set of keys and append to result_. filter_offsets_.push_back(result_.size()); - policy_->CreateFilter(&tmp_keys_[0], num_keys, &result_); + policy_->CreateFilter(&tmp_keys_[0], static_cast(num_keys), &result_); tmp_keys_.clear(); keys_.clear(); @@ -97,7 +97,7 @@ bool FilterBlockReader::KeyMayMatch(uint64_t block_offset, const Slice& key) { if (index < num_) { uint32_t start = DecodeFixed32(offset_ + index*4); uint32_t limit = DecodeFixed32(offset_ + index*4 + 4); - if (start <= limit && limit <= (offset_ - data_)) { + if (start <= limit && limit <= static_cast(offset_ - data_)) { Slice filter = Slice(data_ + start, limit - start); return policy_->KeyMayMatch(key, filter); } else if (start == limit) { diff --git a/src/leveldb/table/format.cc b/src/leveldb/table/format.cc index aa63144c..285e1c0d 100644 --- a/src/leveldb/table/format.cc +++ b/src/leveldb/table/format.cc @@ -30,15 +30,14 @@ Status BlockHandle::DecodeFrom(Slice* input) { } void Footer::EncodeTo(std::string* dst) const { -#ifndef NDEBUG const size_t original_size = dst->size(); -#endif metaindex_handle_.EncodeTo(dst); index_handle_.EncodeTo(dst); dst->resize(2 * BlockHandle::kMaxEncodedLength); // Padding PutFixed32(dst, static_cast(kTableMagicNumber & 0xffffffffu)); PutFixed32(dst, static_cast(kTableMagicNumber >> 32)); assert(dst->size() == original_size + kEncodedLength); + (void)original_size; // Disable unused variable warning. } Status Footer::DecodeFrom(Slice* input) { @@ -83,7 +82,7 @@ Status ReadBlock(RandomAccessFile* file, } if (contents.size() != n + kBlockTrailerSize) { delete[] buf; - return Status::Corruption("truncated block read"); + return Status::Corruption("truncated block read", file->GetName()); } // Check the crc of the type and the block contents @@ -93,7 +92,7 @@ Status ReadBlock(RandomAccessFile* file, const uint32_t actual = crc32c::Value(data, n + 1); if (actual != crc) { delete[] buf; - s = Status::Corruption("block checksum mismatch"); + s = Status::Corruption("block checksum mismatch", file->GetName()); return s; } } @@ -120,13 +119,13 @@ Status ReadBlock(RandomAccessFile* file, size_t ulength = 0; if (!port::Snappy_GetUncompressedLength(data, n, &ulength)) { delete[] buf; - return Status::Corruption("corrupted compressed block contents"); + return Status::Corruption("corrupted compressed block contents", file->GetName()); } char* ubuf = new char[ulength]; if (!port::Snappy_Uncompress(data, n, ubuf)) { delete[] buf; delete[] ubuf; - return Status::Corruption("corrupted compressed block contents"); + return Status::Corruption("corrupted compressed block contents", file->GetName()); } delete[] buf; result->data = Slice(ubuf, ulength); @@ -136,7 +135,7 @@ Status ReadBlock(RandomAccessFile* file, } default: delete[] buf; - return Status::Corruption("bad block type"); + return Status::Corruption("bad block type", file->GetName()); } return Status::OK(); diff --git a/src/leveldb/table/iterator_wrapper.h b/src/leveldb/table/iterator_wrapper.h index 9e16b3db..f410c3fa 100644 --- a/src/leveldb/table/iterator_wrapper.h +++ b/src/leveldb/table/iterator_wrapper.h @@ -5,6 +5,9 @@ #ifndef STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ #define STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ +#include "leveldb/iterator.h" +#include "leveldb/slice.h" + namespace leveldb { // A internal wrapper class with an interface similar to Iterator that diff --git a/src/leveldb/table/table.cc b/src/leveldb/table/table.cc index dff8a825..decf8082 100644 --- a/src/leveldb/table/table.cc +++ b/src/leveldb/table/table.cc @@ -82,7 +82,7 @@ Status Table::Open(const Options& options, *table = new Table(rep); (*table)->ReadMeta(footer); } else { - if (index_block) delete index_block; + delete index_block; } return s; diff --git a/src/leveldb/table/table_test.cc b/src/leveldb/table/table_test.cc index c723bf84..abf6e246 100644 --- a/src/leveldb/table/table_test.cc +++ b/src/leveldb/table/table_test.cc @@ -853,12 +853,20 @@ TEST(TableTest, ApproximateOffsetOfCompressed) { options.compression = kSnappyCompression; c.Finish(options, &keys, &kvmap); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 2000, 3000)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 2000, 3000)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 4000, 6000)); + // Expected upper and lower bounds of space used by compressible strings. + static const int kSlop = 1000; // Compressor effectiveness varies. + const int expected = 2500; // 10000 * compression ratio (0.25) + const int min_z = expected - kSlop; + const int max_z = expected + kSlop; + + ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, kSlop)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, kSlop)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, kSlop)); + // Have now emitted a large compressible string, so adjust expected offset. + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), min_z, max_z)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), min_z, max_z)); + // Have now emitted two large compressible strings, so adjust expected offset. + ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 2 * min_z, 2 * max_z)); } } // namespace leveldb diff --git a/src/leveldb/util/arena.cc b/src/leveldb/util/arena.cc index 9367f714..74078213 100644 --- a/src/leveldb/util/arena.cc +++ b/src/leveldb/util/arena.cc @@ -9,8 +9,7 @@ namespace leveldb { static const int kBlockSize = 4096; -Arena::Arena() { - blocks_memory_ = 0; +Arena::Arena() : memory_usage_(0) { alloc_ptr_ = NULL; // First allocation will allocate a block alloc_bytes_remaining_ = 0; } @@ -60,8 +59,9 @@ char* Arena::AllocateAligned(size_t bytes) { char* Arena::AllocateNewBlock(size_t block_bytes) { char* result = new char[block_bytes]; - blocks_memory_ += block_bytes; blocks_.push_back(result); + memory_usage_.NoBarrier_Store( + reinterpret_cast(MemoryUsage() + block_bytes + sizeof(char*))); return result; } diff --git a/src/leveldb/util/arena.h b/src/leveldb/util/arena.h index 73bbf1cb..48bab337 100644 --- a/src/leveldb/util/arena.h +++ b/src/leveldb/util/arena.h @@ -9,6 +9,7 @@ #include #include #include +#include "port/port.h" namespace leveldb { @@ -24,10 +25,9 @@ class Arena { char* AllocateAligned(size_t bytes); // Returns an estimate of the total memory usage of data allocated - // by the arena (including space allocated but not yet used for user - // allocations). + // by the arena. size_t MemoryUsage() const { - return blocks_memory_ + blocks_.capacity() * sizeof(char*); + return reinterpret_cast(memory_usage_.NoBarrier_Load()); } private: @@ -41,8 +41,8 @@ class Arena { // Array of new[] allocated memory blocks std::vector blocks_; - // Bytes of memory in blocks allocated so far - size_t blocks_memory_; + // Total memory usage of the arena. + port::AtomicPointer memory_usage_; // No copying allowed Arena(const Arena&); diff --git a/src/leveldb/util/bloom.cc b/src/leveldb/util/bloom.cc index a27a2ace..bf3e4ca6 100644 --- a/src/leveldb/util/bloom.cc +++ b/src/leveldb/util/bloom.cc @@ -47,7 +47,7 @@ class BloomFilterPolicy : public FilterPolicy { dst->resize(init_size + bytes, 0); dst->push_back(static_cast(k_)); // Remember # of probes in filter char* array = &(*dst)[init_size]; - for (size_t i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { // Use double-hashing to generate a sequence of hash values. // See analysis in [Kirsch,Mitzenmacher 2006]. uint32_t h = BloomHash(keys[i]); diff --git a/src/leveldb/util/bloom_test.cc b/src/leveldb/util/bloom_test.cc index 77fb1b31..1b87a2be 100644 --- a/src/leveldb/util/bloom_test.cc +++ b/src/leveldb/util/bloom_test.cc @@ -46,7 +46,8 @@ class BloomTest { key_slices.push_back(Slice(keys_[i])); } filter_.clear(); - policy_->CreateFilter(&key_slices[0], key_slices.size(), &filter_); + policy_->CreateFilter(&key_slices[0], static_cast(key_slices.size()), + &filter_); keys_.clear(); if (kVerbose >= 2) DumpFilter(); } diff --git a/src/leveldb/util/cache.cc b/src/leveldb/util/cache.cc index 8b197bc0..ce468861 100644 --- a/src/leveldb/util/cache.cc +++ b/src/leveldb/util/cache.cc @@ -19,6 +19,23 @@ Cache::~Cache() { namespace { // LRU cache implementation +// +// Cache entries have an "in_cache" boolean indicating whether the cache has a +// reference on the entry. The only ways that this can become false without the +// entry being passed to its "deleter" are via Erase(), via Insert() when +// an element with a duplicate key is inserted, or on destruction of the cache. +// +// The cache keeps two linked lists of items in the cache. All items in the +// cache are in one list or the other, and never both. Items still referenced +// by clients but erased from the cache are in neither list. The lists are: +// - in-use: contains the items currently referenced by clients, in no +// particular order. (This list is used for invariant checking. If we +// removed the check, elements that would otherwise be on this list could be +// left as disconnected singleton lists.) +// - LRU: contains the items not currently referenced by clients, in LRU order +// Elements are moved between these lists by the Ref() and Unref() methods, +// when they detect an element in the cache acquiring or losing its only +// external reference. // An entry is a variable length heap-allocated structure. Entries // are kept in a circular doubly linked list ordered by access time. @@ -30,7 +47,8 @@ struct LRUHandle { LRUHandle* prev; size_t charge; // TODO(opt): Only allow uint32_t? size_t key_length; - uint32_t refs; + bool in_cache; // Whether entry is in the cache. + uint32_t refs; // References, including cache reference, if present. uint32_t hash; // Hash of key(); used for fast sharding and comparisons char key_data[1]; // Beginning of key @@ -147,49 +165,77 @@ class LRUCache { Cache::Handle* Lookup(const Slice& key, uint32_t hash); void Release(Cache::Handle* handle); void Erase(const Slice& key, uint32_t hash); + void Prune(); + size_t TotalCharge() const { + MutexLock l(&mutex_); + return usage_; + } private: void LRU_Remove(LRUHandle* e); - void LRU_Append(LRUHandle* e); + void LRU_Append(LRUHandle*list, LRUHandle* e); + void Ref(LRUHandle* e); void Unref(LRUHandle* e); + bool FinishErase(LRUHandle* e); // Initialized before use. size_t capacity_; // mutex_ protects the following state. - port::Mutex mutex_; + mutable port::Mutex mutex_; size_t usage_; // Dummy head of LRU list. // lru.prev is newest entry, lru.next is oldest entry. + // Entries have refs==1 and in_cache==true. LRUHandle lru_; + // Dummy head of in-use list. + // Entries are in use by clients, and have refs >= 2 and in_cache==true. + LRUHandle in_use_; + HandleTable table_; }; LRUCache::LRUCache() : usage_(0) { - // Make empty circular linked list + // Make empty circular linked lists. lru_.next = &lru_; lru_.prev = &lru_; + in_use_.next = &in_use_; + in_use_.prev = &in_use_; } LRUCache::~LRUCache() { + assert(in_use_.next == &in_use_); // Error if caller has an unreleased handle for (LRUHandle* e = lru_.next; e != &lru_; ) { LRUHandle* next = e->next; - assert(e->refs == 1); // Error if caller has an unreleased handle + assert(e->in_cache); + e->in_cache = false; + assert(e->refs == 1); // Invariant of lru_ list. Unref(e); e = next; } } +void LRUCache::Ref(LRUHandle* e) { + if (e->refs == 1 && e->in_cache) { // If on lru_ list, move to in_use_ list. + LRU_Remove(e); + LRU_Append(&in_use_, e); + } + e->refs++; +} + void LRUCache::Unref(LRUHandle* e) { assert(e->refs > 0); e->refs--; - if (e->refs <= 0) { - usage_ -= e->charge; + if (e->refs == 0) { // Deallocate. + assert(!e->in_cache); (*e->deleter)(e->key(), e->value); free(e); + } else if (e->in_cache && e->refs == 1) { // No longer in use; move to lru_ list. + LRU_Remove(e); + LRU_Append(&lru_, e); } } @@ -198,10 +244,10 @@ void LRUCache::LRU_Remove(LRUHandle* e) { e->prev->next = e->next; } -void LRUCache::LRU_Append(LRUHandle* e) { - // Make "e" newest entry by inserting just before lru_ - e->next = &lru_; - e->prev = lru_.prev; +void LRUCache::LRU_Append(LRUHandle* list, LRUHandle* e) { + // Make "e" newest entry by inserting just before *list + e->next = list; + e->prev = list->prev; e->prev->next = e; e->next->prev = e; } @@ -210,9 +256,7 @@ Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) { MutexLock l(&mutex_); LRUHandle* e = table_.Lookup(key, hash); if (e != NULL) { - e->refs++; - LRU_Remove(e); - LRU_Append(e); + Ref(e); } return reinterpret_cast(e); } @@ -234,34 +278,58 @@ Cache::Handle* LRUCache::Insert( e->charge = charge; e->key_length = key.size(); e->hash = hash; - e->refs = 2; // One from LRUCache, one for the returned handle + e->in_cache = false; + e->refs = 1; // for the returned handle. memcpy(e->key_data, key.data(), key.size()); - LRU_Append(e); - usage_ += charge; - LRUHandle* old = table_.Insert(e); - if (old != NULL) { - LRU_Remove(old); - Unref(old); - } + if (capacity_ > 0) { + e->refs++; // for the cache's reference. + e->in_cache = true; + LRU_Append(&in_use_, e); + usage_ += charge; + FinishErase(table_.Insert(e)); + } // else don't cache. (Tests use capacity_==0 to turn off caching.) while (usage_ > capacity_ && lru_.next != &lru_) { LRUHandle* old = lru_.next; - LRU_Remove(old); - table_.Remove(old->key(), old->hash); - Unref(old); + assert(old->refs == 1); + bool erased = FinishErase(table_.Remove(old->key(), old->hash)); + if (!erased) { // to avoid unused variable when compiled NDEBUG + assert(erased); + } } return reinterpret_cast(e); } -void LRUCache::Erase(const Slice& key, uint32_t hash) { - MutexLock l(&mutex_); - LRUHandle* e = table_.Remove(key, hash); +// If e != NULL, finish removing *e from the cache; it has already been removed +// from the hash table. Return whether e != NULL. Requires mutex_ held. +bool LRUCache::FinishErase(LRUHandle* e) { if (e != NULL) { + assert(e->in_cache); LRU_Remove(e); + e->in_cache = false; + usage_ -= e->charge; Unref(e); } + return e != NULL; +} + +void LRUCache::Erase(const Slice& key, uint32_t hash) { + MutexLock l(&mutex_); + FinishErase(table_.Remove(key, hash)); +} + +void LRUCache::Prune() { + MutexLock l(&mutex_); + while (lru_.next != &lru_) { + LRUHandle* e = lru_.next; + assert(e->refs == 1); + bool erased = FinishErase(table_.Remove(e->key(), e->hash)); + if (!erased) { // to avoid unused variable when compiled NDEBUG + assert(erased); + } + } } static const int kNumShardBits = 4; @@ -314,6 +382,18 @@ class ShardedLRUCache : public Cache { MutexLock l(&id_mutex_); return ++(last_id_); } + virtual void Prune() { + for (int s = 0; s < kNumShards; s++) { + shard_[s].Prune(); + } + } + virtual size_t TotalCharge() const { + size_t total = 0; + for (int s = 0; s < kNumShards; s++) { + total += shard_[s].TotalCharge(); + } + return total; + } }; } // end anonymous namespace diff --git a/src/leveldb/util/cache_test.cc b/src/leveldb/util/cache_test.cc index 43716715..468f7a64 100644 --- a/src/leveldb/util/cache_test.cc +++ b/src/leveldb/util/cache_test.cc @@ -59,6 +59,11 @@ class CacheTest { &CacheTest::Deleter)); } + Cache::Handle* InsertAndReturnHandle(int key, int value, int charge = 1) { + return cache_->Insert(EncodeKey(key), EncodeValue(value), charge, + &CacheTest::Deleter); + } + void Erase(int key) { cache_->Erase(EncodeKey(key)); } @@ -135,8 +140,11 @@ TEST(CacheTest, EntriesArePinned) { TEST(CacheTest, EvictionPolicy) { Insert(100, 101); Insert(200, 201); + Insert(300, 301); + Cache::Handle* h = cache_->Lookup(EncodeKey(300)); - // Frequently used entry must be kept around + // Frequently used entry must be kept around, + // as must things that are still in use. for (int i = 0; i < kCacheSize + 100; i++) { Insert(1000+i, 2000+i); ASSERT_EQ(2000+i, Lookup(1000+i)); @@ -144,6 +152,25 @@ TEST(CacheTest, EvictionPolicy) { } ASSERT_EQ(101, Lookup(100)); ASSERT_EQ(-1, Lookup(200)); + ASSERT_EQ(301, Lookup(300)); + cache_->Release(h); +} + +TEST(CacheTest, UseExceedsCacheSize) { + // Overfill the cache, keeping handles on all inserted entries. + std::vector h; + for (int i = 0; i < kCacheSize + 100; i++) { + h.push_back(InsertAndReturnHandle(1000+i, 2000+i)); + } + + // Check that all the entries can be found in the cache. + for (int i = 0; i < h.size(); i++) { + ASSERT_EQ(2000+i, Lookup(1000+i)); + } + + for (int i = 0; i < h.size(); i++) { + cache_->Release(h[i]); + } } TEST(CacheTest, HeavyEntries) { @@ -179,6 +206,19 @@ TEST(CacheTest, NewId) { ASSERT_NE(a, b); } +TEST(CacheTest, Prune) { + Insert(1, 100); + Insert(2, 200); + + Cache::Handle* handle = cache_->Lookup(EncodeKey(1)); + ASSERT_TRUE(handle); + cache_->Prune(); + cache_->Release(handle); + + ASSERT_EQ(100, Lookup(1)); + ASSERT_EQ(-1, Lookup(2)); +} + } // namespace leveldb int main(int argc, char** argv) { diff --git a/src/leveldb/util/crc32c.cc b/src/leveldb/util/crc32c.cc index 6db9e770..b3f40eee 100644 --- a/src/leveldb/util/crc32c.cc +++ b/src/leveldb/util/crc32c.cc @@ -8,6 +8,8 @@ #include "util/crc32c.h" #include + +#include "port/port.h" #include "util/coding.h" namespace leveldb { @@ -283,7 +285,27 @@ static inline uint32_t LE_LOAD32(const uint8_t *p) { return DecodeFixed32(reinterpret_cast(p)); } +// Determine if the CPU running this program can accelerate the CRC32C +// calculation. +static bool CanAccelerateCRC32C() { + if (!port::HasAcceleratedCRC32C()) + return false; + + // Double-check that the accelerated implementation functions correctly. + // port::AcceleretedCRC32C returns zero when unable to accelerate. + static const char kTestCRCBuffer[] = "TestCRCBuffer"; + static const char kBufSize = sizeof(kTestCRCBuffer) - 1; + static const uint32_t kTestCRCValue = 0xdcbc59fa; + + return port::AcceleratedCRC32C(0, kTestCRCBuffer, kBufSize) == kTestCRCValue; +} + uint32_t Extend(uint32_t crc, const char* buf, size_t size) { + static bool accelerate = CanAccelerateCRC32C(); + if (accelerate) { + return port::AcceleratedCRC32C(crc, buf, size); + } + const uint8_t *p = reinterpret_cast(buf); const uint8_t *e = p + size; uint32_t l = crc ^ 0xffffffffu; diff --git a/src/leveldb/util/env.cc b/src/leveldb/util/env.cc index c2600e96..c58a0821 100644 --- a/src/leveldb/util/env.cc +++ b/src/leveldb/util/env.cc @@ -9,6 +9,10 @@ namespace leveldb { Env::~Env() { } +Status Env::NewAppendableFile(const std::string& fname, WritableFile** result) { + return Status::NotSupported("NewAppendableFile", fname); +} + SequentialFile::~SequentialFile() { } diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc index ba266786..f7791831 100644 --- a/src/leveldb/util/env_posix.cc +++ b/src/leveldb/util/env_posix.cc @@ -11,12 +11,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include "leveldb/env.h" #include "leveldb/slice.h" @@ -24,15 +26,70 @@ #include "util/logging.h" #include "util/mutexlock.h" #include "util/posix_logger.h" +#include "util/env_posix_test_helper.h" namespace leveldb { namespace { +static int open_read_only_file_limit = -1; +static int mmap_limit = -1; + static Status IOError(const std::string& context, int err_number) { return Status::IOError(context, strerror(err_number)); } +// Helper class to limit resource usage to avoid exhaustion. +// Currently used to limit read-only file descriptors and mmap file usage +// so that we do not end up running out of file descriptors, virtual memory, +// or running into kernel performance problems for very large databases. +class Limiter { + public: + // Limit maximum number of resources to |n|. + Limiter(intptr_t n) { + SetAllowed(n); + } + + // If another resource is available, acquire it and return true. + // Else return false. + bool Acquire() { + if (GetAllowed() <= 0) { + return false; + } + MutexLock l(&mu_); + intptr_t x = GetAllowed(); + if (x <= 0) { + return false; + } else { + SetAllowed(x - 1); + return true; + } + } + + // Release a resource acquired by a previous call to Acquire() that returned + // true. + void Release() { + MutexLock l(&mu_); + SetAllowed(GetAllowed() + 1); + } + + private: + port::Mutex mu_; + port::AtomicPointer allowed_; + + intptr_t GetAllowed() const { + return reinterpret_cast(allowed_.Acquire_Load()); + } + + // REQUIRES: mu_ must be held + void SetAllowed(intptr_t v) { + allowed_.Release_Store(reinterpret_cast(v)); + } + + Limiter(const Limiter&); + void operator=(const Limiter&); +}; + class PosixSequentialFile: public SequentialFile { private: std::string filename_; @@ -64,79 +121,61 @@ class PosixSequentialFile: public SequentialFile { } return Status::OK(); } + + virtual std::string GetName() const { return filename_; } }; // pread() based random-access class PosixRandomAccessFile: public RandomAccessFile { private: std::string filename_; + bool temporary_fd_; // If true, fd_ is -1 and we open on every read. int fd_; + Limiter* limiter_; public: - PosixRandomAccessFile(const std::string& fname, int fd) - : filename_(fname), fd_(fd) { } - virtual ~PosixRandomAccessFile() { close(fd_); } + PosixRandomAccessFile(const std::string& fname, int fd, Limiter* limiter) + : filename_(fname), fd_(fd), limiter_(limiter) { + temporary_fd_ = !limiter->Acquire(); + if (temporary_fd_) { + // Open file on every access. + close(fd_); + fd_ = -1; + } + } + + virtual ~PosixRandomAccessFile() { + if (!temporary_fd_) { + close(fd_); + limiter_->Release(); + } + } virtual Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const { + int fd = fd_; + if (temporary_fd_) { + fd = open(filename_.c_str(), O_RDONLY); + if (fd < 0) { + return IOError(filename_, errno); + } + } + Status s; - ssize_t r = pread(fd_, scratch, n, static_cast(offset)); + ssize_t r = pread(fd, scratch, n, static_cast(offset)); *result = Slice(scratch, (r < 0) ? 0 : r); if (r < 0) { // An error: return a non-ok status s = IOError(filename_, errno); } - return s; - } -}; - -// Helper class to limit mmap file usage so that we do not end up -// running out virtual memory or running into kernel performance -// problems for very large databases. -class MmapLimiter { - public: - // Up to 1000 mmaps for 64-bit binaries; none for smaller pointer sizes. - MmapLimiter() { - SetAllowed(sizeof(void*) >= 8 ? 1000 : 0); - } - - // If another mmap slot is available, acquire it and return true. - // Else return false. - bool Acquire() { - if (GetAllowed() <= 0) { - return false; - } - MutexLock l(&mu_); - intptr_t x = GetAllowed(); - if (x <= 0) { - return false; - } else { - SetAllowed(x - 1); - return true; + if (temporary_fd_) { + // Close the temporary file descriptor opened earlier. + close(fd); } + return s; } - // Release a slot acquired by a previous call to Acquire() that returned true. - void Release() { - MutexLock l(&mu_); - SetAllowed(GetAllowed() + 1); - } - - private: - port::Mutex mu_; - port::AtomicPointer allowed_; - - intptr_t GetAllowed() const { - return reinterpret_cast(allowed_.Acquire_Load()); - } - - // REQUIRES: mu_ must be held - void SetAllowed(intptr_t v) { - allowed_.Release_Store(reinterpret_cast(v)); - } - - MmapLimiter(const MmapLimiter&); - void operator=(const MmapLimiter&); + virtual std::string GetName() const { return filename_; } }; // mmap() based random-access @@ -145,12 +184,12 @@ class PosixMmapReadableFile: public RandomAccessFile { std::string filename_; void* mmapped_region_; size_t length_; - MmapLimiter* limiter_; + Limiter* limiter_; public: // base[0,length-1] contains the mmapped contents of the file. PosixMmapReadableFile(const std::string& fname, void* base, size_t length, - MmapLimiter* limiter) + Limiter* limiter) : filename_(fname), mmapped_region_(base), length_(length), limiter_(limiter) { } @@ -171,6 +210,8 @@ class PosixMmapReadableFile: public RandomAccessFile { } return s; } + + virtual std::string GetName() const { return filename_; } }; class PosixWritableFile : public WritableFile { @@ -231,7 +272,7 @@ class PosixWritableFile : public WritableFile { if (fd < 0) { s = IOError(dir, errno); } else { - if (fsync(fd) < 0) { + if (fsync(fd) < 0 && errno != EINVAL) { s = IOError(dir, errno); } close(fd); @@ -252,6 +293,8 @@ class PosixWritableFile : public WritableFile { } return s; } + + virtual std::string GetName() const { return filename_; } }; static int LockOrUnlock(int fd, bool lock) { @@ -333,7 +376,7 @@ class PosixEnv : public Env { mmap_limit_.Release(); } } else { - *result = new PosixRandomAccessFile(fname, fd); + *result = new PosixRandomAccessFile(fname, fd, &fd_limit_); } return s; } @@ -351,6 +394,19 @@ class PosixEnv : public Env { return s; } + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result) { + Status s; + FILE* f = fopen(fname.c_str(), "a"); + if (f == NULL) { + *result = NULL; + s = IOError(fname, errno); + } else { + *result = new PosixWritableFile(fname, f); + } + return s; + } + virtual bool FileExists(const std::string& fname) { return access(fname.c_str(), F_OK) == 0; } @@ -520,10 +576,42 @@ class PosixEnv : public Env { BGQueue queue_; PosixLockTable locks_; - MmapLimiter mmap_limit_; + Limiter mmap_limit_; + Limiter fd_limit_; }; -PosixEnv::PosixEnv() : started_bgthread_(false) { +// Return the maximum number of concurrent mmaps. +static int MaxMmaps() { + if (mmap_limit >= 0) { + return mmap_limit; + } + // Up to 4096 mmaps for 64-bit binaries; none for smaller pointer sizes. + mmap_limit = sizeof(void*) >= 8 ? 4096 : 0; + return mmap_limit; +} + +// Return the maximum number of read-only files to keep open. +static intptr_t MaxOpenFiles() { + if (open_read_only_file_limit >= 0) { + return open_read_only_file_limit; + } + struct rlimit rlim; + if (getrlimit(RLIMIT_NOFILE, &rlim)) { + // getrlimit failed, fallback to hard-coded default. + open_read_only_file_limit = 50; + } else if (rlim.rlim_cur == RLIM_INFINITY) { + open_read_only_file_limit = std::numeric_limits::max(); + } else { + // Allow use of 20% of available file descriptors for read-only files. + open_read_only_file_limit = rlim.rlim_cur / 5; + } + return open_read_only_file_limit; +} + +PosixEnv::PosixEnv() + : started_bgthread_(false), + mmap_limit_(MaxMmaps()), + fd_limit_(MaxOpenFiles()) { PthreadCall("mutex_init", pthread_mutex_init(&mu_, NULL)); PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, NULL)); } @@ -598,6 +686,16 @@ static pthread_once_t once = PTHREAD_ONCE_INIT; static Env* default_env; static void InitDefaultEnv() { default_env = new PosixEnv; } +void EnvPosixTestHelper::SetReadOnlyFDLimit(int limit) { + assert(default_env == NULL); + open_read_only_file_limit = limit; +} + +void EnvPosixTestHelper::SetReadOnlyMMapLimit(int limit) { + assert(default_env == NULL); + mmap_limit = limit; +} + Env* Env::Default() { pthread_once(&once, InitDefaultEnv); return default_env; diff --git a/src/leveldb/util/env_posix_test.cc b/src/leveldb/util/env_posix_test.cc new file mode 100644 index 00000000..295f8ae4 --- /dev/null +++ b/src/leveldb/util/env_posix_test.cc @@ -0,0 +1,66 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/env.h" + +#include "port/port.h" +#include "util/testharness.h" +#include "util/env_posix_test_helper.h" + +namespace leveldb { + +static const int kDelayMicros = 100000; +static const int kReadOnlyFileLimit = 4; +static const int kMMapLimit = 4; + +class EnvPosixTest { + public: + Env* env_; + EnvPosixTest() : env_(Env::Default()) { } + + static void SetFileLimits(int read_only_file_limit, int mmap_limit) { + EnvPosixTestHelper::SetReadOnlyFDLimit(read_only_file_limit); + EnvPosixTestHelper::SetReadOnlyMMapLimit(mmap_limit); + } +}; + +TEST(EnvPosixTest, TestOpenOnRead) { + // Write some test data to a single file that will be opened |n| times. + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string test_file = test_dir + "/open_on_read.txt"; + + FILE* f = fopen(test_file.c_str(), "w"); + ASSERT_TRUE(f != NULL); + const char kFileData[] = "abcdefghijklmnopqrstuvwxyz"; + fputs(kFileData, f); + fclose(f); + + // Open test file some number above the sum of the two limits to force + // open-on-read behavior of POSIX Env leveldb::RandomAccessFile. + const int kNumFiles = kReadOnlyFileLimit + kMMapLimit + 5; + leveldb::RandomAccessFile* files[kNumFiles] = {0}; + for (int i = 0; i < kNumFiles; i++) { + ASSERT_OK(env_->NewRandomAccessFile(test_file, &files[i])); + } + char scratch; + Slice read_result; + for (int i = 0; i < kNumFiles; i++) { + ASSERT_OK(files[i]->Read(i, 1, &read_result, &scratch)); + ASSERT_EQ(kFileData[i], read_result[0]); + } + for (int i = 0; i < kNumFiles; i++) { + delete files[i]; + } + ASSERT_OK(env_->DeleteFile(test_file)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + // All tests currently run with the same read-only file limits. + leveldb::EnvPosixTest::SetFileLimits(leveldb::kReadOnlyFileLimit, + leveldb::kMMapLimit); + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/env_posix_test_helper.h b/src/leveldb/util/env_posix_test_helper.h new file mode 100644 index 00000000..03869605 --- /dev/null +++ b/src/leveldb/util/env_posix_test_helper.h @@ -0,0 +1,28 @@ +// Copyright 2017 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_ +#define STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_ + +namespace leveldb { + +class EnvPosixTest; + +// A helper for the POSIX Env to facilitate testing. +class EnvPosixTestHelper { + private: + friend class EnvPosixTest; + + // Set the maximum number of read-only files that will be opened. + // Must be called before creating an Env. + static void SetReadOnlyFDLimit(int limit); + + // Set the maximum number of read-only files that will be mapped via mmap. + // Must be called before creating an Env. + static void SetReadOnlyMMapLimit(int limit); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_ diff --git a/src/leveldb/util/env_test.cc b/src/leveldb/util/env_test.cc index b72cb443..839ae56a 100644 --- a/src/leveldb/util/env_test.cc +++ b/src/leveldb/util/env_test.cc @@ -10,29 +10,31 @@ namespace leveldb { static const int kDelayMicros = 100000; +static const int kReadOnlyFileLimit = 4; +static const int kMMapLimit = 4; -class EnvPosixTest { +class EnvTest { private: port::Mutex mu_; std::string events_; public: Env* env_; - EnvPosixTest() : env_(Env::Default()) { } + EnvTest() : env_(Env::Default()) { } }; static void SetBool(void* ptr) { reinterpret_cast(ptr)->NoBarrier_Store(ptr); } -TEST(EnvPosixTest, RunImmediately) { +TEST(EnvTest, RunImmediately) { port::AtomicPointer called (NULL); env_->Schedule(&SetBool, &called); - Env::Default()->SleepForMicroseconds(kDelayMicros); + env_->SleepForMicroseconds(kDelayMicros); ASSERT_TRUE(called.NoBarrier_Load() != NULL); } -TEST(EnvPosixTest, RunMany) { +TEST(EnvTest, RunMany) { port::AtomicPointer last_id (NULL); struct CB { @@ -59,7 +61,7 @@ TEST(EnvPosixTest, RunMany) { env_->Schedule(&CB::Run, &cb3); env_->Schedule(&CB::Run, &cb4); - Env::Default()->SleepForMicroseconds(kDelayMicros); + env_->SleepForMicroseconds(kDelayMicros); void* cur = last_id.Acquire_Load(); ASSERT_EQ(4, reinterpret_cast(cur)); } @@ -78,7 +80,7 @@ static void ThreadBody(void* arg) { s->mu.Unlock(); } -TEST(EnvPosixTest, StartThread) { +TEST(EnvTest, StartThread) { State state; state.val = 0; state.num_running = 3; @@ -92,7 +94,7 @@ TEST(EnvPosixTest, StartThread) { if (num == 0) { break; } - Env::Default()->SleepForMicroseconds(kDelayMicros); + env_->SleepForMicroseconds(kDelayMicros); } ASSERT_EQ(state.val, 3); } diff --git a/src/leveldb/util/env_win.cc b/src/leveldb/util/env_win.cc index e11a96b7..830332ab 100644 --- a/src/leveldb/util/env_win.cc +++ b/src/leveldb/util/env_win.cc @@ -1,7 +1,7 @@ // This file contains source that originates from: // http://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/env_win32.h // http://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/port_win32.cc -// Those files dont' have any explict license headers but the +// Those files don't have any explicit license headers but the // project (http://code.google.com/p/leveldbwin/) lists the 'New BSD License' // as the license. #if defined(LEVELDB_PLATFORM_WINDOWS) @@ -78,6 +78,7 @@ class Win32SequentialFile : public SequentialFile virtual Status Read(size_t n, Slice* result, char* scratch); virtual Status Skip(uint64_t n); BOOL isEnable(); + virtual std::string GetName() const { return _filename; } private: BOOL _Init(); void _CleanUp(); @@ -94,6 +95,7 @@ class Win32RandomAccessFile : public RandomAccessFile virtual ~Win32RandomAccessFile(); virtual Status Read(uint64_t offset, size_t n, Slice* result,char* scratch) const; BOOL isEnable(); + virtual std::string GetName() const { return _filename; } private: BOOL _Init(LPCWSTR path); void _CleanUp(); @@ -106,7 +108,7 @@ class Win32RandomAccessFile : public RandomAccessFile class Win32WritableFile : public WritableFile { public: - Win32WritableFile(const std::string& fname); + Win32WritableFile(const std::string& fname, bool append); ~Win32WritableFile(); virtual Status Append(const Slice& data); @@ -114,6 +116,7 @@ class Win32WritableFile : public WritableFile virtual Status Flush(); virtual Status Sync(); BOOL isEnable(); + virtual std::string GetName() const { return filename_; } private: std::string filename_; ::HANDLE _hFile; @@ -158,6 +161,8 @@ class Win32Env : public Env RandomAccessFile** result); virtual Status NewWritableFile(const std::string& fname, WritableFile** result); + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result); virtual bool FileExists(const std::string& fname); @@ -198,24 +203,16 @@ class Win32Env : public Env void ToWidePath(const std::string& value, std::wstring& target) { wchar_t buffer[MAX_PATH]; - MultiByteToWideChar(CP_ACP, 0, value.c_str(), -1, buffer, MAX_PATH); + MultiByteToWideChar(CP_UTF8, 0, value.c_str(), -1, buffer, MAX_PATH); target = buffer; } void ToNarrowPath(const std::wstring& value, std::string& target) { char buffer[MAX_PATH]; - WideCharToMultiByte(CP_ACP, 0, value.c_str(), -1, buffer, MAX_PATH, NULL, NULL); + WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, buffer, MAX_PATH, NULL, NULL); target = buffer; } -std::string GetCurrentDir() -{ - CHAR path[MAX_PATH]; - ::GetModuleFileNameA(::GetModuleHandleA(NULL),path,MAX_PATH); - *strrchr(path,'\\') = 0; - return std::string(path); -} - std::wstring GetCurrentDirW() { WCHAR path[MAX_PATH]; @@ -224,6 +221,13 @@ std::wstring GetCurrentDirW() return std::wstring(path); } +std::string GetCurrentDir() +{ + std::string path; + ToNarrowPath(GetCurrentDirW(), path); + return path; +} + std::string& ModifyPath(std::string& path) { if(path[0] == '/' || path[0] == '\\'){ @@ -353,11 +357,13 @@ BOOL Win32SequentialFile::_Init() ToWidePath(_filename, path); _hFile = CreateFileW(path.c_str(), GENERIC_READ, - FILE_SHARE_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (_hFile == INVALID_HANDLE_VALUE) + _hFile = NULL; return _hFile ? TRUE : FALSE; } @@ -401,7 +407,7 @@ BOOL Win32RandomAccessFile::_Init( LPCWSTR path ) { BOOL bRet = FALSE; if(!_hFile) - _hFile = ::CreateFileW(path,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING, + _hFile = ::CreateFileW(path,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,NULL); if(!_hFile || _hFile == INVALID_HANDLE_VALUE ) _hFile = NULL; @@ -423,17 +429,23 @@ void Win32RandomAccessFile::_CleanUp() } } -Win32WritableFile::Win32WritableFile(const std::string& fname) +Win32WritableFile::Win32WritableFile(const std::string& fname, bool append) : filename_(fname) { std::wstring path; ToWidePath(fname, path); - DWORD Flag = PathFileExistsW(path.c_str()) ? OPEN_EXISTING : CREATE_ALWAYS; + // NewAppendableFile: append to an existing file, or create a new one + // if none exists - this is OPEN_ALWAYS behavior, with + // FILE_APPEND_DATA to avoid having to manually position the file + // pointer at the end of the file. + // NewWritableFile: create a new file, delete if it exists - this is + // CREATE_ALWAYS behavior. This file is used for writing only so + // use GENERIC_WRITE. _hFile = CreateFileW(path.c_str(), - GENERIC_READ | GENERIC_WRITE, + append ? FILE_APPEND_DATA : GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE, NULL, - Flag, + append ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); // CreateFileW returns INVALID_HANDLE_VALUE in case of error, always check isEnable() before use @@ -661,7 +673,7 @@ Status Win32Env::GetFileSize( const std::string& fname, uint64_t* file_size ) ToWidePath(ModifyPath(path), wpath); HANDLE file = ::CreateFileW(wpath.c_str(), - GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); + GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); LARGE_INTEGER li; if(::GetFileSizeEx(file,&li)){ *file_size = (uint64_t)li.QuadPart; @@ -751,14 +763,16 @@ uint64_t Win32Env::NowMicros() static Status CreateDirInner( const std::string& dirname ) { Status sRet; - DWORD attr = ::GetFileAttributes(dirname.c_str()); + std::wstring dirnameW; + ToWidePath(dirname, dirnameW); + DWORD attr = ::GetFileAttributesW(dirnameW.c_str()); if (attr == INVALID_FILE_ATTRIBUTES) { // doesn't exist: std::size_t slash = dirname.find_last_of("\\"); if (slash != std::string::npos){ sRet = CreateDirInner(dirname.substr(0, slash)); if (!sRet.ok()) return sRet; } - BOOL result = ::CreateDirectory(dirname.c_str(), NULL); + BOOL result = ::CreateDirectoryW(dirnameW.c_str(), NULL); if (result == FALSE) { sRet = Status::IOError(dirname, "Could not create directory."); return sRet; @@ -823,7 +837,9 @@ Status Win32Env::NewLogger( const std::string& fname, Logger** result ) { Status sRet; std::string path = fname; - Win32WritableFile* pMapFile = new Win32WritableFile(ModifyPath(path)); + // Logs are opened with write semantics, not with append semantics + // (see PosixEnv::NewLogger) + Win32WritableFile* pMapFile = new Win32WritableFile(ModifyPath(path), false); if(!pMapFile->isEnable()){ delete pMapFile; *result = NULL; @@ -837,7 +853,20 @@ Status Win32Env::NewWritableFile( const std::string& fname, WritableFile** resul { Status sRet; std::string path = fname; - Win32WritableFile* pFile = new Win32WritableFile(ModifyPath(path)); + Win32WritableFile* pFile = new Win32WritableFile(ModifyPath(path), false); + if(!pFile->isEnable()){ + *result = NULL; + sRet = Status::IOError(fname,Win32::GetLastErrSz()); + }else + *result = pFile; + return sRet; +} + +Status Win32Env::NewAppendableFile( const std::string& fname, WritableFile** result ) +{ + Status sRet; + std::string path = fname; + Win32WritableFile* pFile = new Win32WritableFile(ModifyPath(path), true); if(!pFile->isEnable()){ *result = NULL; sRet = Status::IOError(fname,Win32::GetLastErrSz()); diff --git a/src/leveldb/util/logging.cc b/src/leveldb/util/logging.cc index ca6b3244..db6160c8 100644 --- a/src/leveldb/util/logging.cc +++ b/src/leveldb/util/logging.cc @@ -49,7 +49,7 @@ bool ConsumeDecimalNumber(Slice* in, uint64_t* val) { uint64_t v = 0; int digits = 0; while (!in->empty()) { - char c = (*in)[0]; + unsigned char c = (*in)[0]; if (c >= '0' && c <= '9') { ++digits; const int delta = (c - '0'); diff --git a/src/leveldb/util/options.cc b/src/leveldb/util/options.cc index 76af5b93..b5e62276 100644 --- a/src/leveldb/util/options.cc +++ b/src/leveldb/util/options.cc @@ -21,9 +21,10 @@ Options::Options() block_cache(NULL), block_size(4096), block_restart_interval(16), + max_file_size(2<<20), compression(kSnappyCompression), + reuse_logs(false), filter_policy(NULL) { } - } // namespace leveldb diff --git a/src/leveldb/util/testutil.h b/src/leveldb/util/testutil.h index adad3fc1..d7e45837 100644 --- a/src/leveldb/util/testutil.h +++ b/src/leveldb/util/testutil.h @@ -45,6 +45,16 @@ class ErrorEnv : public EnvWrapper { } return target()->NewWritableFile(fname, result); } + + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result) { + if (writable_file_error_) { + ++num_writable_file_errors_; + *result = NULL; + return Status::IOError(fname, "fake error"); + } + return target()->NewAppendableFile(fname, result); + } }; } // namespace test diff --git a/src/miner.cpp b/src/miner.cpp index 57fd2d74..b745e10d 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -1036,17 +1036,22 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight, { //if ( !isStake || ASSETCHAINS_STAKED != 0 ) { - if (!reservekey.GetReservedKey(pubkey)) - { - return NULL; - } - scriptPubKey.resize(35); - ptr = (uint8_t *)pubkey.begin(); - scriptPubKey[0] = 33; - for (i=0; i<33; i++) { - scriptPubKey[i+1] = ptr[i]; + if (!GetBoolArg("-disablewallet", false)) { + // wallet enabled + if (!reservekey.GetReservedKey(pubkey)) + return NULL; + scriptPubKey.clear(); + scriptPubKey = CScript() << ToByteVector(pubkey) << OP_CHECKSIG; + } else { + // wallet disabled + CTxDestination dest = DecodeDestination(GetArg("-mineraddress", "")); + if (IsValidDestination(dest)) { + // CKeyID keyID = boost::get(dest); + // scriptPubKey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG; + scriptPubKey = GetScriptForDestination(dest); + } else + return NULL; } - scriptPubKey[34] = OP_CHECKSIG; } } if ( ASSETCHAINS_MARMARA != 0 && nHeight > 0 && (nHeight & 1) == 0 ) diff --git a/src/qt/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm index 102adce6..5eb23c76 100644 --- a/src/qt/macdockiconhandler.mm +++ b/src/qt/macdockiconhandler.mm @@ -1,12 +1,11 @@ -// Copyright (c) 2011-2018 The Bitcoin Core developers +// Copyright (c) 2011-2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "macdockiconhandler.h" -#undef slots -#include -#include +#include +#include static MacDockIconHandler *s_instance = nullptr; @@ -21,9 +20,7 @@ bool dockClickHandler(id self, SEL _cmd, ...) { } void setupDockClickHandler() { - id app = objc_msgSend((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication")); - id delegate = objc_msgSend(app, sel_registerName("delegate")); - Class delClass = (Class)objc_msgSend(delegate, sel_registerName("class")); + Class delClass = (Class)[[[NSApplication sharedApplication] delegate] class]; SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:"); class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:"); } @@ -44,3 +41,13 @@ void setupDockClickHandler() { { delete s_instance; } + +/** + * Force application activation on macOS. With Qt 5.5.1 this is required when + * an action in the Dock menu is triggered. + * TODO: Define a Qt version where it's no-longer necessary. + */ +void ForceActivation() +{ + [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; +} diff --git a/src/serialize.h b/src/serialize.h index 3d9c3fae..722b1bab 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -542,6 +542,7 @@ template inline void Unserialize(St * vectors of unsigned char are a special case and are intended to be serialized as a single opaque blob. */ template void Serialize_impl(Stream& os, const std::vector& v, const unsigned char&); +template void Serialize_impl(Stream& os, const std::vector& v, const bool&); template void Serialize_impl(Stream& os, const std::vector& v, const V&); template inline void Serialize(Stream& os, const std::vector& v); template void Unserialize_impl(Stream& is, std::vector& v, const unsigned char&); @@ -718,6 +719,18 @@ void Serialize_impl(Stream& os, const std::vector& v, const unsigned char& os.write((char*)&v[0], v.size() * sizeof(T)); } +template +void Serialize_impl(Stream& os, const std::vector& v, const bool&) +{ + // A special case for std::vector, as dereferencing + // std::vector::const_iterator does not result in a const bool& + // due to std::vector's special casing for bool arguments. + WriteCompactSize(os, v.size()); + for (bool elem : v) { + ::Serialize(os, elem); + } +} + template void Serialize_impl(Stream& os, const std::vector& v, const V&) { diff --git a/zcutil/build-linux.sh b/zcutil/build-linux.sh deleted file mode 100755 index b500bfd5..00000000 --- a/zcutil/build-linux.sh +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env bash - -set -eu -o pipefail - -function cmd_pref() { - if type -p "$2" > /dev/null; then - eval "$1=$2" - else - eval "$1=$3" - fi -} - -# If a g-prefixed version of the command exists, use it preferentially. -function gprefix() { - cmd_pref "$1" "g$2" "$2" -} - -gprefix READLINK readlink -cd "$(dirname "$("$READLINK" -f "$0")")/.." - -# Allow user overrides to $MAKE. Typical usage for users who need it: -# MAKE=gmake ./zcutil/build.sh -j$(nproc) -if [[ -z "${MAKE-}" ]]; then - MAKE=make -fi - -# Allow overrides to $BUILD and $HOST for porters. Most users will not need it. -# BUILD=i686-pc-linux-gnu ./zcutil/build.sh -if [[ -z "${BUILD-}" ]]; then - BUILD="$(./depends/config.guess)" -fi -if [[ -z "${HOST-}" ]]; then - HOST="$BUILD" -fi - -# Allow users to set arbitrary compile flags. Most users will not need this. -if [[ -z "${CONFIGURE_FLAGS-}" ]]; then - CONFIGURE_FLAGS="" -fi - -if [ "x$*" = 'x--help' ] -then - cat <